<html>

<head>build ai saas app</head>
<body>
<div>
	<p>Have you ever wanted to build a real software as a service application with AI features and payments and credit system that you might even turn into a site income or a business idea? Well, today you'll have the chance to build an app with AI generative fill, image restoration, recoloring and object and background removal.</p><p>
 
By watching this video, you'll learn the fundamentals of building AI SaaS applications and you can even use it as a blueprint for your own SaaS or as a great addition to portfolio and skill set.</p><p>
 Now, let me show you how the app works.</p><p>
 The homepage showcases image transformations that people in the community have made.</p><p>
 At the bottom, we have a pagination component to navigate between different pages.</p><p>
 But what's even more interesting is the search feature at the top.</p><p>
 It's not your typical search where you look for images by their title.</p><p>
 Instead, you search based on what's in the image.</p><p>
 Let's say you wanted to find all the images containing the sky, just type it in and there you have it.</p><p>
 Isn't that incredible? This is an advanced search functionality.</p><p>
 You probably have never implemented before.</p><p>
 Now, to be able to create your own image transformations, you need to log in.</p><p>
 And for that, you'll develop an entire user management system with clerk.</p><p>
 If I click on one of these community images created by someone else, it will take me to its details page.</p><p>
 And here, I can compare the image changes and even download it.</p><p>
 But where's the fun in looking at other people's images? Let's create our own unique images instead.</p><p>
 First, we have AI imagery store where we simply provide a title, upload our image, handvoilat.</p><p>
 We have a beautifully restored image.</p><p>
 We can save it for future use or download and discard it.</p><p>
 Moving on, we have a generative fill where we can adjust the aspect ratio of the uploaded image and generatively fill the space to resemble the original image.</p><p>
 It's perfect when you want to turn a horizontal image into a vertical one and vice versa.</p><p>
 See how nicely AI did this.</p><p>
 Next, we have object removal where we can specify which object we want to remove from the image by describing it and boom.</p><p>
 It's gone.</p><p>
 The fourth one is object recolor, which works similarly to object removal.</p><p>
 By the way, you can upload your own images or select from a wide range of free to use images from unsplash or any other platform here.</p><p>
 Simply describe the object you want to recolor and watch the magic happen.</p><p>
 All thanks to AI.</p><p>
 Last but not least, we have a straightforward AI background removal for any image.</p><p>
 You can save these images and update them whenever you want by just adjusting your image on this spot.</p><p>
 Pretty neat, right? Implementing these complex features is all possible thanks to cloud-dinneries incredible AI tools, which we'll use to build this app.</p><p>
 And the amazing part is that it is absolutely free to use.</p><p>
 Besides cloud-dinneries, we'll also use next GS, Tailwind and Chatsien, TypeScript, Clurk, MongoDB and Stripe to build this app.</p><p>
 So make sure you have a good grasp of JavaScript and React before watching the tutorial.</p><p>
 If not, check out our crash courses on YouTube.</p><p>
 Back to the app, there's also a profile page to view all the transformations you've saved.</p><p>
 Now, here's the feature that makes this a SaaS.</p><p>
 As I showcase these demos, I used up all my free credits.</p><p>
 So if I try to create something new, you'll see a nice looking model saying we don't have enough credits.</p><p>
 We can either buy more or just leave it.</p><p>
 If you click on it, we'll go to the credits page.</p><p>
 And there, we can choose how many credits we want to buy, and then we'll head to our payment process or Stripe.</p><p>
 After paying, we'll see how many credits we have back on the profile page.</p><p>
 And now, when I try to create something, it works.</p><p>
 Pretty cool, right? All these amazing AI features are bundled with a robust payment credit system.</p><p>
 Remember, you can use this app as a blueprint for building your own SaaS applications.</p><p>
 I bet you can't wait to start developing it.</p><p>
 So, let's get started.</p><p>
 To get started with building our phenomenal project, we'll start from bare beginnings.</p><p>
 And that is by creating a new Empty folder on our desktop.</p><p>
 I'm going to call it, Imagineify, but feel free to choose a better name.</p><p>
 Then, simply drag and drop it into your Empty Visual Studio Code window.</p><p>
 Once you're in there, open up your terminal, and we are ready to initialize next.</p><p>
js.</p><p>
 The React framework for the web.</p><p>
 Used by some of the world's largest companies, it enables you to create high-quality web applications.</p><p>
 So, simply follow my steps by running this command.</p><p>
 That is, mpx, create next app, add latest.</p><p>
slash to initialize it in the current directory.</p><p>
 It will ask you a couple of questions, and you want to just stop enter because we're going to use all of the default options, which are recommended.</p><p>
 Now, this process takes about a minute, so feel free to pause this video, and I'll be right back.</p><p>
 And there we go.</p><p>
 All of the files and folders have been initialized and set up right within our folder.</p><p>
 We're going to check them out soon, but before we do that, let's install and set up Shatsien.</p><p>
 Shatsien allows you to build your own component library.</p><p>
 It provides a lot of components out of the box, for dashboards, cards, tasks, playgrounds, forms, and more.</p><p>
 And the best thing is, they're all bare bones.</p><p>
 Very simple, but Shatsien uses tailwind CSS behind the scenes, so you can naturally style them to match any design you want.</p><p>
 So, to set up Shatsien, simply run mpx, Shatsien-UI, add latest, space init.</p><p>
 This will run the Shatsien configuration.</p><p>
 Choose which style you would like, in this case its default, SlateColor will do just fine, and we can also choose the default option of using CSS variables for colors.</p><p>
 It will take just a moment, and there we go, Shatsien has been installed as well.</p><p>
 Now that the bare bones of our project have been installed, let's run mpmrandev to check it out in the browser.</p><p>
 You can hold command or control and then click this link to open it up within your browser.</p><p>
 Once you're in there, you should be able to see something that looks like this.</p><p>
 It's just a default next JS page.</p><p>
 Now let's work around it to provide all of the necessary fonts, colors, and styles that will need for building out our application.</p><p>
 To do that, we can start with the layout file of our application, which is right here we have some metadata.</p><p>
 And in this case, here you can add the title of your application.</p><p>
 I'm going to go with Imaginify.</p><p>
 Of course, I invite you to choose a better name.</p><p>
 And we can also add a description.</p><p>
 In this case, it's going to be an AI-powered image generator.</p><p>
 Instead of using the basic inter font, we want to use a font called IBMplexSans.</p><p>
 So right at the top you can import IBM, underscoreplex, underscoreSans, coming from next font Google.</p><p>
 We can then initialize its constructor right here by running IBMplexSans.</p><p>
 And we're going to choose the subset of Latin.</p><p>
 But we're going to also add a couple of additional things in here, such as different weights.</p><p>
 That's going to be of 400.</p><p>
 We can also get 500, 600, and 700.</p><p>
 And you don't have to say weights, just weight is fine.</p><p>
 Finally, we can choose variable.</p><p>
 This is going to be a variable that will trigger or turn on our font.</p><p>
 So we can say dash-font, dash-ibm, dashplex.</p><p>
 And then we can use it right here within the body.</p><p>
 So under the class name, we can call a special utility function called CN.</p><p>
 You can see that the intelligence will allow you to automatically import it from lib utils.</p><p>
 And if we quickly check it out right here, you'll notice that this utils file was created for us.</p><p>
 It simply allows you to merge a couple of class names together.</p><p>
 So with that in mind, let's also rename our font to IBMplex.</p><p>
 And we can now use it right here within our class names.</p><p>
 By saying class name is CN, which we call as a function.</p><p>
 And then we can pass a couple of different class names, such as font dash, IBMplex.</p><p>
 And then anti-aliased.</p><p>
 This is a CSS property that simply makes some fonts easier to read.</p><p>
 Finally, right next to that string, we're going to call the IBMplexSans font.</p><p>
 And then the dot variable on it, which will apply the actual font.</p><p>
 And we can put children in your line.</p><p>
 So it's easier to see what's happening.</p><p>
 Essentially, we've just modified the metadata and applied our font.</p><p>
 The next thing we want to do is set up our base tailwind CSS config.</p><p>
 And we can do that by going to tailwindconfig.</p><p>
ts.</p><p>
 And then in the description of this video, you can find a readme containing all of the code blocks for this project.</p><p>
 Go to that readme and then find the final tailwind.</p><p>
config.</p><p>
tsx, copy it, and then override it.</p><p>
 Copy it and then override everything we have here.</p><p>
 You'll notice that in here, we simply set some background images and the font family with declared, as well as some default colors.</p><p>
 As you can see right here, so we don't have to refer to all of these absolute values.</p><p>
 We can just use these nice color names.</p><p>
 Once we have the tailwind config set up, the other thing we want to set up is the globals.</p><p>
css file.</p><p>
 That is our second style file, so you can navigate to it.</p><p>
 And similarly, like you copied the tailwind config, you can also copy and override the globals.</p><p>
css.</p><p>
 Here, we have some class names that are mostly utility classes that will help us style our application.</p><p>
 Keep in mind, you will still be doing all of the logic and styling yourself.</p><p>
 These are just some helper classes that we can use within our application to make our life easier.</p><p>
 And of course, what would our app be without some images? That's why I've prepared a complete folder of all of the assets we'll be using throughout this project for you.</p><p>
 Just go to the same readme, download the zipped public folder, unzip it, delete the existing one that we have here, and then simply drag and drop the new one right here and say copy folder.</p><p>
 You'll notice that in here, we have a lot of assets such as icons, which are all SVGs, and also some images like banners and stacked coins, which we're going to use for credits later on.</p><p>
 Another thing we have in here is the Favicon, which we want to move over to our app and say replace.</p><p>
 This is a new Favicon, which has these imaginify icons right here, like sparks, which means that something AI related is happening.</p><p>
 And once you do that, we can check out our application in the browser.</p><p>
 It seems that not too much has changed, but if you open up your tab, you can see that we are using a proper title, as well as the Favicon for our application.</p><p>
 Now, you might be wondering which browser this is.</p><p>
 This is called the ARC browser.</p><p>
 It's a bit different from traditional browsers, and if you haven't tried it out yet, I would highly recommend you to do so.</p><p>
 They're not sponsoring this video or anything, but as you guys know, I always want to teach you things that can improve your developer productivity, and using ARC is one of them.</p><p>
 With that said, we have now imported all of the necessary CSS files and assets, which means that we can finally start focusing on the initial structure of our application.</p><p>
 Now, even though I like looking at this next JS template, let's remove it, and let's turn this local host 3000 into something special.</p><p>
 We'll do that by first learning about next JS routing, creating route groups, and creating different layouts for each one of these route groups.</p><p>
 I want to teach you the proper file and folder structure within next JS.</p><p>
 So, let's get started by creating a couple of route groups.</p><p>
 In the app directory, nested folders are normally mapped to different URL paths.</p><p>
 For example, if you have a folder named Users, you'll be able to go to forward slash users.</p><p>
 However, you can mark a folder as a route group to prevent the folder from being included in the routes URL.</p><p>
 And this allows you to organize your route segments and project files into different logical groups without affecting the URL structure.</p><p>
 It's very useful when you want to organize routes into groups, for example, by authentication, user management, and so on, or if you want to enable nested layouts for all the routes within the same route group.</p><p>
 And I'm going to teach you both of these concepts right here.</p><p>
 So, how do we create a route group? You simply have to create a folder wrapped in parentheses.</p><p>
 That is it.</p><p>
 And in practice, that looks something like this.</p><p>
 You can have marketing, shop, and then different pages within it.</p><p>
 Enough theory.</p><p>
 Let's get into practice.</p><p>
 The first route group will create will be for our authentication.</p><p>
 So, let's create a new folder and let's call it parentheses auth.</p><p>
 And the right within it will create a new layout dot TSX file.</p><p>
 Within there, we can run RAFC e, which will just create a basic structure of a React component.</p><p>
 If RAFC e didn't work for you, that must mean that you don't have the ES7 plus React Redux React Native snippets extension installed.</p><p>
 So, just install it and then it will work flawlessly.</p><p>
 Now, let's rename this layout to a capital layout like this.</p><p>
 And within here, we can return an HTML5 semantic main tag to indicate that we'll have some main content within here.</p><p>
 We can accept children right here through props and immediately define the type of those children has children.</p><p>
 Is of a type React.</p><p>
React node.</p><p>
 That's because layouts always have to export some children within them.</p><p>
 So, our main will have a class name of auth and right within it, we want to render children.</p><p>
 Great.</p><p>
 Now, since we talked about route groups, let's also talk a bit about layouts.</p><p>
 So, I can simply go to next year's documentation, which is great by the way, and search for layout.</p><p>
 And we can do a command F and search for layout one more time.</p><p>
 Right here, we can see more information about what a layout is.</p><p>
 And I know that I could rush through this video and just teach you how to build this phenomenal application.</p><p>
 But I always like to take my time and explain things in detail.</p><p>
 I am not here to teach you how to build this specific app.</p><p>
 I'm here to teach you how to build any app.</p><p>
 You know that saying, give a man a fish and feeding for a day.</p><p>
 Teach a man how to fish and feed him for a lifetime.</p><p>
 And that's exactly what I want to do in this case as well.</p><p>
 I want to teach you how to read documentation, how to be an independent developer and learn on your own.</p><p>
 So, that's why we're taking the time to go through the docs together.</p><p>
 And this content will always be free on YouTube.</p><p>
 But in case you want to dive a step further, we also have the official JS Mastery that Pro Platform.</p><p>
 Where, if you want to advance your next JS skills, you can take our ultimate next JS course.</p><p>
 And only if you're already a junior developer and you want to get to mid and senior positions, you can sign up for the master class.</p><p>
 Right now, there's only a waiting list and that's because big things are coming.</p><p>
 But with that said, let me explain what a layout is.</p><p>
 A layout will typically export the children and then allow you to put some shared UI for different routes here, such as the navigation bar or the footer.</p><p>
 So for example, you don't want your authentication routes to have the footer and the nav bar because they're auth pages.</p><p>
 In that case, you will create a layout without anything.</p><p>
 But then for your homepage routes, you want to have the nav bar and footer within every single route.</p><p>
 So instead of simply duplicating it within every single route, you will just place it within its layout.</p><p>
 So, let's check it out in practice.</p><p>
 Our auth layout will be completely empty.</p><p>
 Whereas we'll create a new route group called root, which is going to be for our home routes.</p><p>
 And then within there, we can create a new layout.</p><p>
tsx.</p><p>
 We can start by duplicating the one we already have here, hand-pasting it.</p><p>
 But this one will have a class name of root.</p><p>
 And it won't immediately render children.</p><p>
 Rather, we'll have some additional structure within it, such as a div that will have a class name equal to root-container.</p><p>
 We will also have another div right within it, which will have a class name equal to wrapper.</p><p>
 And finally, we can put the children right within that wrapper.</p><p>
 So let's place it here.</p><p>
 There we go.</p><p>
 We have the wrapper and we have the children.</p><p>
 Finally, every route group also has to render a page.</p><p>
 So, let's move this basic page that was created for us by next GS into the root layout.</p><p>
 Let's remove everything within it, run RAFCE, and just call it home.</p><p>
 This will be our home page.</p><p>
 And the reason we moved it here is because we want the home page to have this specific layout.</p><p>
 So, now that that is done, I'm going to close all the files to have a cleaner working environment and collapse it.</p><p>
 And I did that by holding command and then pressing W.</p><p>
 And let's check out our app within the browser.</p><p>
 There we go.</p><p>
 You should be able to see just a simple home on a white screen.</p><p>
 Now that we have that, let's also create a couple more routes within the root route group.</p><p>
 So let's create a new folder called credits.</p><p>
 And within it, create a new page.</p><p>
tsex, where you can simply run RAFCE and rename it to credit.</p><p>
 We'll use this later on to purchase the credits for AI image generation.</p><p>
 The second route we'll have within the root route group will be a profile route.</p><p>
 Or you'll be able to see your profile.</p><p>
 So we can also create a page.</p><p>
tsex, run RAFCE, and call it profile page.</p><p>
 The next route we will create is a new folder called transformations.</p><p>
 So let's do it like this.</p><p>
 And here we'll showcase the detail of the created image transformation.</p><p>
 So let's create a new page.</p><p>
tsex, where we can run RAFCE, and we can call it transformations page.</p><p>
 Now here's a key part with these transformations.</p><p>
 Each transformation will be different.</p><p>
 So it won't simply happen on localhost 3000, forward slash transformations.</p><p>
 Like this.</p><p>
 That's not going to be the case.</p><p>
 Rather, what will happen is that each transformation will have its own ID.</p><p>
 So it's going to look something like this.</p><p>
 And to implement this, we need to use a concept known as dynamic routes within next GS.</p><p>
 And doing dynamic routes is pretty simple.</p><p>
 You basically have to wrap your folders name within square brackets, instead of parentheses this time.</p><p>
 For example, square brackets ID.</p><p>
 And then you will get access to that specific ID program through the program's prop passed into your page.</p><p>
 So how would that look like in practice? We can create a new folder within transformations called ID within square brackets, and then move this page within that folder.</p><p>
 That's going to look something like this.</p><p>
 And then as we discussed, you will get access to that specific ID by getting the programs and then getting the ID, which we can talk about later on.</p><p>
 This is the most important part right now.</p><p>
 As right now, we're in the process of creating a routing structure.</p><p>
 This will be a route for the get request, but we also want to have one like that for the update request.</p><p>
 So we can create a new folder within the square brackets ID, which is going to be called update.</p><p>
 And within the update, we want to create a new page.</p><p>
tsx, where we can run RAFCE, and we can call it update transformation page.</p><p>
 There we go.</p><p>
 And finally, there's one more, which will be added just to the transformations folder.</p><p>
 So we can create a new folder on it called add.</p><p>
 And then within add will create a new dynamic route of type within square brackets, which will allow us to create different types of image transformations.</p><p>
 And within the type, we can create a new page.</p><p>
tsx, in which we can run RAFCE, and call it add transformation type page.</p><p>
 There we go.</p><p>
 Better descriptive than shorter.</p><p>
 Trust me, because if you come back to this code, you know exactly what it does.</p><p>
 But if you used a shorter name, then you might be a bit confused.</p><p>
 But with that said, we now have a complete routing structure for our application.</p><p>
 And how does that look like in practice? But the question is, did that change anything in the browser? And the answer is yes.</p><p>
 If I zoom this in, you'll see the homepage, and we can now navigate to forward slash profile.</p><p>
 And it changes the profile page.</p><p>
 We can also navigate to transformations.</p><p>
 And then forward slash one to three, because this is a specific transformation page.</p><p>
 We can also navigate to transformations, and then ID, and then update in case you want to update that specific transformation.</p><p>
 And you can see that we have a route for that as well.</p><p>
 And all of that was done automatically for us by using the next GSS file based routing.</p><p>
 You create folders and pages, and instantly you have the complete routing without having to set up everything, like you usually would by using a React router package if you were using only React.</p><p>
 So with that in mind, you've now learned the basics as well as more advanced concepts of next GSS routing, like file based routing, route groups, and creating different layouts for each one of these route groups.</p><p>
 And you've learned a bit about creating a proper file and folder structure, great work.</p><p>
 And now that you've set up your routing, it's time that we implement authentication.</p><p>
 And for that, we'll use the most comprehensive user management platform, Clirk.</p><p>
 It's not just a sign in box.</p><p>
 It allows you to embed different UI APIs and admin dashboards to authenticate and manage your users.</p><p>
 So to follow my process exactly, click the Clirk link in the description of this video, and then start building for free.</p><p>
 You can keep using Clirk completely for free until you pass 10,000 monthly active users.</p><p>
 And at that point, you can also pay them a bit because you'll be making money from your app as well.</p><p>
 So let's start building for free.</p><p>
 It will ask you to sign in or create an account.</p><p>
 And once you're in, you can add a new application.</p><p>
 Then you can enter your application name.</p><p>
 I'm going to use JSM underscore, imagine if I.</p><p>
.</p><p>
.</p><p>
 You can use JSM underscore any other name.</p><p>
 And immediately, you can choose from a dozen of different sign-in options.</p><p>
 In this case, we can go with email, Google, and let's also add GitHub since we're developers.</p><p>
 Finally, press create application.</p><p>
 And immediately from the kickstart, copy your environment variables.</p><p>
 Back within our application, create a new file in the root of your directory called .</p><p>
env.</p><p>
local.</p><p>
 And then paste what you copied.</p><p>
 This allows us to keep your secrets private.</p><p>
 Back within Clirk, go to configure, users and authentication, and then email phone and username.</p><p>
 Here, we can turn on the username so we know how to call our users once they sign into our application.</p><p>
 And don't forget to click apply changes.</p><p>
 Let's go back to home.</p><p>
 And then let's click continuing docs to follow the Clirk documentation that allows you to add auth and user management to your app within seven minutes.</p><p>
 This will allow us to install Clirk, set up our keys, wrap our app with a clerk provider, limit access to authenticated users, which is very important, and then embed the user button.</p><p>
 So let's go ahead and follow the steps.</p><p>
 First things first, let's install addClirk forward slash next JS.</p><p>
 And then we can open up the terminal, split it, and then simply paste.</p><p>
 MPM install addClirk forward slash next JS.</p><p>
 While that is installing, let's scroll through the next steps.</p><p>
 We have set environment keys, which we have already done.</p><p>
 And then the next step is to wrap our app within the clerk provider.</p><p>
 And before we do that, let's switch this over to dark mode so everything is in dark mode.</p><p>
 The eyes don't burn as they switch from code over to the docs.</p><p>
 So the clerk provider component provides active session and user context to clerk's hooks and other components.</p><p>
 Simply imported by importing clerk provider from clerk next to the top of your file.</p><p>
 And in this case, we need to do it within the layout.</p><p>
 So let's not copy anything as we already have our layouts, but let's just figure out what we need to do, import clerk and then wrap it.</p><p>
 And now the question is within which of the three layouts that we already have, the auth layout, the root layout, and the base layout, will we wrap our auth provider? That's a tough question.</p><p>
 And it's a bit misleading.</p><p>
 If you said within auth, you'd be wrong.</p><p>
 And that's because, yes, even though we'll have some of our auth pages within here, we want to wrap the entire app with a clerk provider to know which pages we can't access and to be able to get access to the user data within all the pages, no matter where we add.</p><p>
 So let's open up the base layout, TSEX right here.</p><p>
 And let's wrap our app with a clerk provider.</p><p>
 It's going to automatically import it if you simply press enter.</p><p>
 We can wrap it and properly close it right here at the bottom and indent this.</p><p>
 The import should have automatically happened from clerk next to yes.</p><p>
 That step was simple.</p><p>
 Let's see what's happening at step four.</p><p>
 Now that clerk is installed and mounted in your application, you can decide which pages are public and which should require authentication to access.</p><p>
 And to make this happen, we need to create a middleware.</p><p>
ts file.</p><p>
 So let's copy it from the docs and create it right here within the root of our directory right next to .</p><p>
env.</p><p>
local by creating a new file called middleware.</p><p>
ts and we can paste what we copied.</p><p>
 In this case, we won't be modifying anything.</p><p>
 We'll just keep it default as it is.</p><p>
 Now let's see if it works as they say it does.</p><p>
 What happens if you try accessing your app by visiting the localhost 3000? If I reload on localhost 3000, you'll see that we are redirected to clerk authentication, which is exactly what we wanted.</p><p>
 Now these auth pages are not within our localhost right now, which means that we have to create route pages within our routing structure so that this window appears directly within our routing structure.</p><p>
 And we can do that by going to our app, our auth, and creating a new folder right here called sign up like this.</p><p>
 And then within the sign up, we can create a new folder, which is going to be called double square bracket, da da da sign dash up and close it with a double square bracket.</p><p>
 And within it, create a new page dot.</p><p>
tsx, a bit of a weird structure, but it works.</p><p>
 And then run r a f c e within it, call it sign up page.</p><p>
 And then it, the only thing we want to return will be sign up, which is a self closing component, which we can automatically import directly from ad clerk forward slash next.</p><p>
 Now you can copy this entire file because we're going to also use it for the sign in.</p><p>
 So let's create the same structure for sign in within our auth, we can create a new folder called sign dash in within it.</p><p>
 And then it another folder dash dash da da, sign dash in, close it.</p><p>
 And within it, create a new page dot.</p><p>
tsx where you can paste what we copied.</p><p>
 Of course, you want to rename it to sign in.</p><p>
 There we go.</p><p>
 And rename sign up page to sign in page.</p><p>
 This is the only thing you have to do to create your own auth pages within clerk.</p><p>
 Now the last thing you need to do to make it work is head to dot e and v dot local and add a few more variables.</p><p>
 And those will be the paths to your pages.</p><p>
 So you need to call them next public clerk sign in url is equal to forward slash sign dash in duplicated.</p><p>
 Rename this to sign up url and then replace this with up and then due to more, which is going to be next public clerk.</p><p>
 And then add the word after sign up and after sign in.</p><p>
 Where do we want to navigate? And in this case, it's just going to be forward slash in both of these cases.</p><p>
 Finally, to test this out, we can follow one of the last steps from clerk's docs, which is using their prebuilt component of user button that allows users to manage their account information, completing this full authentication circle.</p><p>
 So within our homepage, let's use this user button.</p><p>
 I'm going to navigate over to root page, which is this one right here.</p><p>
 By the way, to quickly switch the files, I use the command P keystroke, which allows you to start typing the name of the file you want to go to, press enter and you're immediately there.</p><p>
 That's just another pro tip.</p><p>
 Now within here, we can wrap everything in a div.</p><p>
 We can modify this home to be a p tag like this.</p><p>
 And the right within it, we can render the user button component coming from clerk next.</p><p>
 Yes, and we can give it a prop of after sign out URL is going to be forward slash.</p><p>
 So this is the URL that the user is navigated to once they sign out.</p><p>
 And if we check out the docs, now you can sign out of your application and sign in.</p><p>
 So that technically means that you are authenticated.</p><p>
 Let's go ahead and test it in practice.</p><p>
 Let's go ahead and test it within our local host 3000.</p><p>
 We immediately get redirected to our sign in page, which is within our own application now.</p><p>
 The sign in window is at the top left right now, but don't worry about that.</p><p>
 We're going to fix it soon.</p><p>
 For now, let's continue with GitHub, Google or just your email.</p><p>
 I remember how hard it is to usually turn Google authentication on.</p><p>
 You have to go to the Google Cloud Developer Console, get your ID, and do all sorts of other things.</p><p>
 And right here, it just works out of the box.</p><p>
 I just signed in with my Gmail account, and it's also asking me for my username.</p><p>
 So right here, I'm going to say JavaScript, mastery, and press continue.</p><p>
 And immediately, we are within our homepage.</p><p>
 You can see that now we're authenticated.</p><p>
 And we also have this icon pulled directly from Google that allows us to open up this clerk window and where you can sign out, see your account details, as well as manage your account.</p><p>
 Out of the box, you can manage your account and change profile information, update your username, email address, and even see connected accounts, as well as manage your password.</p><p>
 Think about how long it would take you to develop all of this manually.</p><p>
 It's just not worth the effort, as even if you did it manually, first of all, you're dealing with security here, which is not a good thing to quickly get done.</p><p>
 And second of all, you wouldn't be able to have a beautiful UI, such as this one.</p><p>
 So when clerks set seven minutes, they truly meant it.</p><p>
 Now, while we're finalizing the auth, let's customize clerks' appearance with the branding of our application.</p><p>
 That's also something that I want to teach you, as it's very important to have that custom UI feel, no matter the design of your application.</p><p>
 So right here on the left side, go to customization, and then navigate to branding.</p><p>
 Here, you can modify your application name, and upload the logo, as well as the icon.</p><p>
 You can find all of these within the assets folder that I provided to you.</p><p>
 They'll be right within the public folder of our application.</p><p>
 So let's choose the logo for the first one.</p><p>
 There we go.</p><p>
 And let's choose the icon for the second one.</p><p>
 There we go.</p><p>
 That's better.</p><p>
 Now, before we check out how that looks in action, let's apply our brand color to all clerks' components, simply by passing the appearance prop to the clerks' provider.</p><p>
 The clerks' provider is within our base layout, and it can accept the appearance property, which can take in an object of variables, and here we can provide a color, primary, which is going to be hash, 624, CF5, and you can save it.</p><p>
 Make sure not to have any spaces here, and now let's test it in our browser.</p><p>
 Back on our local host, we can now sign out, and on the next sign in, you can see that now there's a logo here.</p><p>
 And if you've been paying close attention, the color of the button also changed.</p><p>
 And to finalize it, let's zoom it out, and let's center it, as well as change the background color.</p><p>
 We can do that by navigating to our globals.</p><p>
css, and then right here below the layer base, we can add a new class of the dot auth, and we can apply a couple of tailing properties, or tailing classes, such as flex-center to center it, min-h-screen, so it takes the full height of the screen, w-full, so it takes the full width, and bg-purple-100.</p><p>
 This will apply a dot auth class to our auth layout, which will result in this beautiful sign-in interface, and we can also move between sign-in and sign-up, and we can seamlessly log in into our application.</p><p>
 So in this module, you've learned a lot.</p><p>
 You've learned what the color is, and how to integrate it within our application.</p><p>
 You've learned how to protect specific routes, in this case all routes are protected.</p><p>
 You've learned how to set up the default sign-in and sign-up clerk routes, and I've even taught you something that a lot of people think is impossible, which is styling your clerk components.</p><p>
 With that said, the only thing we have right here is an empty screen.</p><p>
 So the next thing we'll do is create the entire front-end interface for application, components such as sidebar, navbar, and even mobile nav.</p><p>
 To get started creating our layout, we can navigate to the routes layout.</p><p>
 Because this is where all of the pages within the application will be.</p><p>
 Here we have the main with the route, and then we have all the children.</p><p>
 But on top of that, we want to render two things.</p><p>
 And the first one is a sidebar component, self-closing like this, and the second one will be a mobile navigation.</p><p>
 So let's create both of these components one by one, starting with a sidebar component.</p><p>
 We can create it by going to our components, and right here creating a new folder within components called shared.</p><p>
 Components shared across the entire application.</p><p>
 Within shared, we can create a file called sidebar.</p><p>
tsex.</p><p>
 Within there, we can run RAFC to initialize our sidebar component.</p><p>
 Then we can go back to the layout, uncomment the sidebar, and imported from components shared sidebar.</p><p>
 Finally, let's start developing the sidebar by turning it into an aside element.</p><p>
 This is an HTML5 semantic tag that usually means that something is on the side, as it says right here, and since it's a sidebar, it makes sense.</p><p>
 We can give it a class name of sidebar, and this class name is coming directly from our globals.</p><p>
 So in case you're wondering which styles exactly will be applied once you add this class name, you can go right here to search, search for the sidebar, and then find it right here.</p><p>
 It will apply property of hidden, each screen for full height, width of 72, white background, some padding, shadows, and then more shadows, and flex on large devices.</p><p>
 Within the aside, we want to create a second wrapper div with a class name equal to flex, size-full, which is going to give it a width of 100 and a height of 100%, flex-column, and a gap of 4.</p><p>
 Within it, we can render our first link.</p><p>
 This link has to be imported from next link, and it will have an href of forward slash, pointing to the homepage, with a class name equal to sidebar-logo, which automatically lets us know what do we put within it, and that's going to be the image, which is a self-closing tag coming from next image, and we can give it a source equal to forward slash assets, forward slash images, forward slash logo dash text dot SVG, with an alt tag of logo, a width of 180, and a height of 28.</p><p>
 If we save it, go back and reload, you'll be able to see our wonderful sidebar on the left side.</p><p>
 Now, within the sidebar, we'll want to mention a lot of different links, pointing to different features of our app, as you usually do with any kind of navigation component, and I don't want to necessarily just type all of these right here, such as link 1, link 2, 3, and so forth.</p><p>
 We want to declare the links in a file called constants.</p><p>
 So let's create a new folder within the root of our directory, called constants, and within it, create a new index dot TS file.</p><p>
 In the same readme where you found other code blocks, you'll also be able to find the constants file.</p><p>
 It is a 200 lines long file, which is seemingly a lot, but it is incredibly simple.</p><p>
 It is basically just exports of all different objects that we'll be using throughout our application, specifically, nav links, which we need right now.</p><p>
 It is just an array of different links, which point to different routes that we created.</p><p>
 They have a label, route, and the icon.</p><p>
 This makes it so much easier now to consume it within the sidebar, rather than to keep all of this right here within the component.</p><p>
 Within the components, only focus on the JSX.</p><p>
 Keep the logic and data in other files.</p><p>
 Now that we have these nav links, we can use them right here below this link by creating a new nav component with a class name equal to sidebar dash nav.</p><p>
 But we only want to render them in case our user is signed in.</p><p>
 For that, we can use the signed in property from clerk next to the JS.</p><p>
 The code within will only show if the user is signed in.</p><p>
 We can then create a new UL, which is an unordered list, with a class name equal to sidebar dash nav underscore elements.</p><p>
 And within, we can finally map over our nav items by saying nav links coming from constants.</p><p>
 That map where we get each individual link, and for each one, we open up a function block.</p><p>
 And first, figure out if that is the link on which we're currently on.</p><p>
 And we can do that by saying const is active is equal to if link that route is triple equal to to the pat name.</p><p>
 That means that we're currently on that link.</p><p>
 The pat name, of course, is coming from use pat name hook provided by next year's.</p><p>
 So we can say const pat name is equal to use pat name coming from next navigation.</p><p>
 Finally, a map is missing a return.</p><p>
 So now that we know whether we're active or not, we can return a new L.</p><p>
I.</p><p>
 item for each one of these links.</p><p>
 Since we're mapping over it, each L.</p><p>
I.</p><p>
 has to have a key, which is going to be equal to link that route since they're unique.</p><p>
 And we can also give it a class name off.</p><p>
 It's going to be a template string where it will always have a sidebar dash nav underscore element property.</p><p>
 It will have a group class name as well.</p><p>
 And then only if it is active, it will have a BG dash purple dash gradient and a text white else.</p><p>
 It will simply have a collar of text dash gray dash 700 within the L.</p><p>
I.</p><p>
 For now, let's simply display a link dot label.</p><p>
 Now we can save it and check it out in action.</p><p>
 But in action, we get a look at this, a meaningful error coming from next years that says use pat name only works in client components.</p><p>
 Add the use client directive at the top of the file to use it.</p><p>
 That is pretty simple.</p><p>
 It means that we're using browser functionalities.</p><p>
 And whenever you do that, you have to add the use client directive at the top, which turned this component from a server side rendered component by default to a client side rendered one.</p><p>
 And if we go back, you can see that we have home, which is currently highlighted.</p><p>
 And then we have all of the other features, which will soon implement.</p><p>
 Let's finalize our L.</p><p>
I.</p><p>
 by not only returning the link label, rather by rendering another link.</p><p>
 So we can actually point somewhere by clicking it.</p><p>
 And this link will have a class name equal to sidebar dash link.</p><p>
 And it will have an each of off link dot route.</p><p>
 Within it, we'll render the image.</p><p>
 Of course, not going to be a basic image.</p><p>
 It will be a next GS image, which has to have a source equal to link that icon.</p><p>
 It has to have an Altag off logo.</p><p>
 It can also have a width of 24, a height of 24 and a class name.</p><p>
 Now we have to be careful.</p><p>
 We're going to give it a bit of brightness, but only if it is active.</p><p>
 We can say if is active and and apply brightness off 200.</p><p>
 And then below that image will render the link dot label.</p><p>
 So now if we save it, you can see how well this looks.</p><p>
 We have a home, which is currently selected.</p><p>
 And we can immediately navigate to all of the different routes simply by clicking on them.</p><p>
 That works because we have already implemented the routes for all of these pages.</p><p>
 And it is so simple to do because we have also created this constant, which is an array containing all of our routes with an icon, route and a label.</p><p>
 So you can see how easy it was to simply map over it.</p><p>
 Now this is what we will see if we're signed in.</p><p>
 But if we're signed out, we need to do something different.</p><p>
 In a similar fashion, we're going to use the signed out component coming from clerk next GS, which simplifies the way of showing some code if the user is signed out.</p><p>
 No more checks at the top of the file and then ensuring if the user is logged in or not, a lot of if statements, we simply put it within the component.</p><p>
 Here we want to render a Shats yen button component.</p><p>
 And looking at the docs, it's just a simple button that displays a component that looks like a button.</p><p>
 If you click it, it clicks as well.</p><p>
 So the reason why I'm showing you this documentation page is to let you know that this will be the first Shats yen component we will install.</p><p>
 Yes, install.</p><p>
 Even though we've set Shats yen up, it doesn't automatically provide you all of those components out of the box, which is a good thing because we won't necessarily need to use all of them.</p><p>
 What we will need to do is just install the ones that we will be using by copying this command or you can type it out with me.</p><p>
 That is MPX Shats yen dash UI, add latest add button.</p><p>
 This component will add a new button within our code base.</p><p>
 You have full control over it.</p><p>
 You can see that a new folder called UI has been created within components and there we have a button.</p><p>
 This might seem like a lot of code for a button, but don't worry, you never have to go through these files created by Shats yen.</p><p>
 You simply use them within our application.</p><p>
 So right here we can render that button, with a capital B importing it from da da slash UI button.</p><p>
 Within it we can render a link and that link will have an href pointing to sign in.</p><p>
 So if we're signed out, we want to point to login.</p><p>
 We can also give this button the as child property, which means that it will be rendered as a link, which is exactly what it is.</p><p>
 And we can give it a class name equal to button BG dash purple, dash gradient and BG dash cover.</p><p>
 Now we cannot see it yet because we're not logged out.</p><p>
 So before we check it out, let's collapse it and let's create a button that allows us to log out.</p><p>
 So still within the URL, right below this map, we can create a final ally that will have a class name of flex dash center, cursor dash pointer, gap dash two, and padding off four.</p><p>
 Within it we can render the user button coming from clear connects to us.</p><p>
 It is a self closing component, which we have used before already.</p><p>
 So we know that we can give it the after sign out URL of just forward slash.</p><p>
 And we can also say show name, which will show the name.</p><p>
 Now we can collapse both the sign in and the signed out.</p><p>
 So it's easier to see what's happening.</p><p>
 We have the logo, we have all of the list of the elements if we're signed in and we have the button to log in if we're signed out.</p><p>
 So let's go ahead and test it all together.</p><p>
 Back within our application, we no longer have the button to check the user profile on the left in the nav bar.</p><p>
 But rather, now it's right here as part of the sidebar, which allows us to sign out as well.</p><p>
 Now at this point in time, we cannot even go back to local host to check it out if we're signed out.</p><p>
 Currently, it's needed to be signed in to check it out.</p><p>
 But later on, we can allow users to just check out what's happening without needing to log in.</p><p>
 So with that in mind, let's just simply sign back in.</p><p>
 Now, even though the current sidebar looks great, there's just so many things to look at.</p><p>
 We don't know what to click, which things to check out first.</p><p>
 So usually what you can see in all of the apps that have a lot of actions is that the top left part are for the actions you can take within the application.</p><p>
 And then the bottom left is usually for some kind of administration, like logging out, buying credits, or checking out your profile.</p><p>
 So let's turn this into two separate lists.</p><p>
 We can do that by navigating into the signed in, and then slicing the links before we display them by saying dot slice from 0 to 6th element.</p><p>
 And then dot map, we do everything the same, and we close the UL right here.</p><p>
 So we close it a bit before.</p><p>
 Then keep in mind, we still have this link right here.</p><p>
 And for that one, we want to open up a new UL element, because we want to separate them into two parts.</p><p>
 So that will be UL class names sidebar nav elements, where we have just an LI with a user button.</p><p>
 So if you go back, that will now push it to the bottom, which looks better.</p><p>
 But we're missing the last two elements from the list.</p><p>
 Because we've sliced only the first six one.</p><p>
 So what we can do is we can copy all of the nav links one more time, not including the UL, and paste them right here within the second UL.</p><p>
 And this time we want to slice from the element six until the end.</p><p>
 And of course, we want to indent it properly.</p><p>
 There we go.</p><p>
 And indent this.</p><p>
 So now if we do that and save, you'll see that on top left, you can see different things that we can do.</p><p>
 And on the bottom left, you can do some admin stuff like checking out your profile, buying credits, or signing out.</p><p>
 Wonderful.</p><p>
 That's it for our sidebar.</p><p>
 Now, let's go ahead and move into creating a mobile navigation bar.</p><p>
 Because what happens if a user is on a mobile phone, they won't have enough space to show the entire sidebar.</p><p>
 To demonstrate that, I've put my browser to the right side and my visuals to the code on the left.</p><p>
 And you can see that our sidebar automatically gets hidden after it loses a specific amount with.</p><p>
 This is for tablet.</p><p>
 And then if you go here, it is gone.</p><p>
 So let's create this mobile navigation bar.</p><p>
 We can do that by navigating over to shared components and creating a new file called mobile nav dot dsx.</p><p>
 There we can run RAFC e save it.</p><p>
 And we can go back to our layout of the route.</p><p>
 And we can uncomment the mobile nav and imported from components shared mobile nav.</p><p>
 Immediately, it appears right here on top of the website.</p><p>
 The next thing we want to do is install another Shatsy and component called sheet.</p><p>
 A sheet extends the dialog component and displays the content that complements the main content of the screen.</p><p>
 Something like this.</p><p>
 You click it and it shows up from the right side.</p><p>
 So we can install it by running this command.</p><p>
 That is MPX Shatsy and UI latest ad sheet.</p><p>
 And immediately here, we can copy its usage as well.</p><p>
 So go to Shatsy and docs and search for sheet.</p><p>
 And there you have the usage.</p><p>
 You can copy all of the imports that they have shared right here and use them within the mobile nav.</p><p>
 We can also copy the entire sheet.</p><p>
 Don't just paste it yet.</p><p>
 Grab the entire mobile nav into a semantic header component that has a class name of header.</p><p>
 That's going to make it look like this.</p><p>
 Within the header, create a link, which of course has to be imported from next link with an href of forward slash pointing to home page.</p><p>
 And a class name equal to flex items dash center gap of two and on medium devices padding why off to as well within there.</p><p>
 We can render a self closing image component.</p><p>
 Of course, being imported from next image that will have a source equal to forward slash assets, forward slash images, forward slash logo dash text dot SVG.</p><p>
 With an alt tag of logo, a width of about 180 and also a height of about 28.</p><p>
 If we save it, you can see this great logo up here on top.</p><p>
 Finally, we want to create a navigation bar right below the link.</p><p>
 So we can call it a nav that will have a class name equal to flex and a gap of two.</p><p>
 Within it, we can render the signed in component, meaning that this only shows if we're signed in immediately.</p><p>
 We want to render the user button coming from clerk.</p><p>
 It will have the after sign out URL to forward slash as well.</p><p>
 If we save it, that's immediately looking good.</p><p>
 There we go.</p><p>
 And then we want to render the sheet component that we copied from Shats Yen.</p><p>
 This thing right here simply paste it, indent it properly and save it.</p><p>
 This is how a sheet looks like.</p><p>
 You click open and it's there.</p><p>
 Now, let's modify the sheet trigger from just a piece of text that says open to an image by saying image, self closing one, that has a source of forward slash assets, forward slash icons, forward slash menu dot SVG, with an outtag of menu, a width of about 32 and a height of 32 and a class name of cursor, dash pointer so we know that it's clickable.</p><p>
 There we go.</p><p>
 That's better.</p><p>
 We have the menu, we click on it and it opens.</p><p>
 Now, right below the sheet trigger, we have the sheet content to which we can give a class name equal to sheet dash content and on small devices width of 64.</p><p>
 If we save it and open it up, there we go.</p><p>
 That's looking good.</p><p>
 We can remove everything that is currently within the sheet content and start from scratch by rendering an empty react fragment.</p><p>
 And within it, we can render an image.</p><p>
 This image will have a source equal to forward slash assets, forward slash images, forward slash logo dash text dot SVG, with an outtag of logo, a width of about 152 and a height of about 23.</p><p>
 If we save it, you can see just the logo because the logo here is being hidden.</p><p>
 So we want to display it again so you know which app you're using.</p><p>
 Right below the image, we want to render all of the links we've had before.</p><p>
 So for that, we can go over to our sidebar by using command P and then pressing sidebar and then enter.</p><p>
 And we can copy the way that we map over the nav link elements.</p><p>
 So we can copy essentially the entire URL right here.</p><p>
 There we go.</p><p>
 And then we can paste it right here below the image.</p><p>
 Of course, indent it properly.</p><p>
 And don't forget to import nav links from constants.</p><p>
 In this case, we don't want to slice them.</p><p>
 We want to show all of them at once.</p><p>
 And we also need to get access to the pat name and for doing that, you know what you need to do.</p><p>
 Const pat name is equal to use pat name coming from next navigation.</p><p>
 Now if we save it, you're going to get an error saying that use pat name only works in client components.</p><p>
 So let's immediately declare this component as use client.</p><p>
 Let's remove the imports which we don't need from UI sheet and put it all in one line.</p><p>
 There we go.</p><p>
 And now if it scroll down and click on it, you can see that we thought we would be able to see something but nothing is there.</p><p>
 So let's modify the class names.</p><p>
 In this case, our URL right here won't have the sidebar nav elements.</p><p>
 Rather, it will have a class name of header nav elements.</p><p>
 Also, we can completely re-modify the class names of this ally.</p><p>
 So let's expand it in multiple lines.</p><p>
 And let's give it a class name equal to a template string immediately putting a dynamic block saying if is active.</p><p>
 And and then give it a class name of gradient text like this.</p><p>
 And then outside of the dynamic block, give it a padding of 18, flex, white space, no wrap and text dash dark dash 700.</p><p>
 If we save it, that's going to make it look quite different.</p><p>
 And in this case, we don't even need to render an image because there's not a lot of space and mobile devices.</p><p>
 So just rendering a label is fine.</p><p>
 And we don't need to provide it a class name of sidebar link.</p><p>
 But now that think about it, we might want to keep the image as it did look better with it.</p><p>
 So let's keep the class name of sidebar link as well as the image right here.</p><p>
 But I believe we can remove the class name so that all of the images are the same.</p><p>
 And then the text is the only thing that changes.</p><p>
 And also, let's give the entire link a cursor of pointer.</p><p>
 If we save it, you can hover over them and you can also renavigate to different screens.</p><p>
 That is great.</p><p>
 Finally, if we're logged out, we want to do almost the same exact thing as we've done in the sidebar, which is simply checking if the user is signed out and rendering a button to sign back in.</p><p>
 So going all the way down below the sheet content and the sheet and the signed in, we want to display the signed out, which we need to import from clerk next she has.</p><p>
 We want to import the button as well.</p><p>
 And if we're signed out, we can point back to signed in.</p><p>
 And now if we click on a specific link, such as the image restore, we can check that it actually modifies in the URL.</p><p>
 Even though we cannot see it right now because the header is covering the title of the page we're on, we can see that the URL changes, which means that our mobile nav bar is working as well.</p><p>
 Also, notice that if you go back to home, you can see double the user button.</p><p>
 That's because we have never removed it from the page, which now we can safely do by removing this user button.</p><p>
 Because we always have one user button, either in the nav bar or if we expand, we have it right here at the bottom, which means that we have successfully implemented the left side bar on desktop devices and the full mobile nav bar on mobile devices.</p><p>
 And in the process of doing so, we've learned a couple of things.</p><p>
 We've learned how to use a sheet component coming from Shats Yen, as well as learned how to use the signed out and signed in components coming from clerk to only show code that should be show to either signed in or signed out users.</p><p>
 And we've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We've learned how to use a sheet component coming from the NaviNom.</p><p>
 We can also keep track of the aspect ratio, which will be of a type string in this case.</p><p>
 We can keep track of the color of the image so we can have a type string.</p><p>
 We can have a prompt use to generate that image, which will be also of a type string.</p><p>
 Very important, we need to have the author that generated that image, which will be of a type schema dot types dot object ID, with a reference to the user.</p><p>
 Of course, this object ID is the type of the author right here.</p><p>
 We want to have a created add date to keep track of the date, which will be just a type date with a default of date dot now.</p><p>
 And we also want to have the updated add, which will be exactly the same thing.</p><p>
 And this gives us our image schema.</p><p>
 But now we want to turn this schema into a model.</p><p>
 And we can do that by saying const image is equal to we check if it already exists under the models.</p><p>
 So models that image.</p><p>
 And this models is coming from Mongoose as well.</p><p>
 So we can import it.</p><p>
 Or if it doesn't exist, we can create a new model with the name of image based off of the image schema.</p><p>
 And this model also has to import it from Mongoose.</p><p>
 Finally, we want to export default that constructor or the model in this case called image.</p><p>
 Now, since we'll be working with typescript, what do you say that we immediately create a type for this image? So that our front end immediately knows what properties do we have on the documents built off of the image schema? We can do that by creating a new interface for the image.</p><p>
 What I like to do in this case is just leverage chat GPT to create it for me.</p><p>
 So I can say create an eye image interface based off of the following image schema.</p><p>
 And then we can feed it all of this information.</p><p>
 And we'll see what it will come up with.</p><p>
 There we go.</p><p>
 Here is how we can define it.</p><p>
 And immediately it started giving us all of the properties.</p><p>
 So let's wait until it is done.</p><p>
 And let's copy this code and paste it right here above.</p><p>
 Of course, you can type it out with me in case you're not using chat GPT for this one.</p><p>
 We have to export this interface as well as we'll be using it within our front end.</p><p>
 And we're going to make it extend the document, which is imported from Mongoose.</p><p>
 This will automatically give it fields like other square fields.</p><p>
 Let's see if chat GPT got it right.</p><p>
 We need a title of a type string.</p><p>
 We need a transformation type of a type string as well.</p><p>
 Public ID string secure URL, which is also going to be a string with which is of a type number height number as well.</p><p>
 Config is going to be an object.</p><p>
 Of course, we could make it a bit more specific if you want to.</p><p>
 We then also have the transformation type ratio, color, prompt, author, which won't be just a string.</p><p>
 Rather, it will be an object where the underscore ID is of a type string.</p><p>
 First name is also of a type string.</p><p>
 And last name is of a type string and created that and updated that our dates.</p><p>
 Great.</p><p>
 So now we have our interface as well as our schema and based off of the schema, we're also getting our model.</p><p>
 Or we can call it a structure for future documents belonging to this schema.</p><p>
 Now I have a challenge for you.</p><p>
 And that is to create a new model this time for the user.</p><p>
 So create a new file called user dot model dot T.</p><p>
 S.</p><p>
 And start creating the same structure like we have here for the user schema.</p><p>
 Make sure that it has fields like clerk ID because we're modifying it with clerk.</p><p>
 Also make sure that it has an email, username, photo, first name, last name, plan ID because they can be a paying user and the credit balance to know how many more image transformations can they make.</p><p>
 Pause this video and take some time to try to do it yourself.</p><p>
 I'm going to provide you with a solution in three, two, one, the complete solution to this exercise or the final user dot model dot T.</p><p>
 S file can be found in the read me of this video.</p><p>
 So simply compare your solution to the final one.</p><p>
 Essentially here we create a new schema with some fields.</p><p>
 And in this case, I didn't even create a tab script interface for it, which you can do and plus points for you if you did that.</p><p>
 But let's just try to follow this structure for now just so we don't have any issues later on once we create these models.</p><p>
 Make sure that it has a clerk ID with a type string required to true and unique to true as well.</p><p>
 Then we have the email, username, photo, first name, last name, plan ID and the credit balance.</p><p>
 Once again, great job for trying it out yourself.</p><p>
 But for now just pays the final solution.</p><p>
 So we're on the same page.</p><p>
 And the last model that we need will be called transaction dot model dot T.</p><p>
 S.</p><p>
 I will also provide this one to you in the read me down below.</p><p>
 So simply copy it and paste it here.</p><p>
 A transaction is used as an additional reference between the user and the image creation because we have to keep track of the credits.</p><p>
 So each transaction is essentially a stripe conversion that we make to turn credits into images.</p><p>
 I hope that makes sense.</p><p>
 If it doesn't, it will make much more sense once we start using it on the front end.</p><p>
 And with that, you've successfully learned how to set up a MongoDB database.</p><p>
 You've learned how to create a MongoDB and Mongoose connection and cash it as well for serverless architecture that next JS offers.</p><p>
 And finally, you've learned how to create three separate MongoDB models.</p><p>
 Now in the next module, we'll be able to start using these models to create their instances such as create new users, create new images and make transactions.</p><p>
 We'll do all of that using next JS server actions.</p><p>
 And when creating a user will sync clerks user data to our backend using something known as a web hook.</p><p>
 So let's get started.</p><p>
 And the first thing we can do are going to be the crowd functionalities for user actions, meaning creating, viewing, updating and deleting users.</p><p>
 And we can do that by starting to create our first actions file.</p><p>
 We can do it within lib and create a new folder called actions utilizing the next JS server actions.</p><p>
 In there, we can create our first file called user dot actions dot t s.</p><p>
 And the most important part to make this a server actions file is to add a use server directive at the top of the file.</p><p>
 This will mark all of the export functions from this file as server actions because they're within a file that uses the use server directive.</p><p>
 And what are server actions in the first place? Well, they are simply asynchronous functions that are executed on the server.</p><p>
 They can be used both in server and client components to handle form submission and data mutations.</p><p>
 But we can also use them for get requests to the only convention is to use the use server directive at the top of the file declare whatever you want to do and then use it within your pages.</p><p>
 Server actions are a simpler alternative to API routes.</p><p>
 We don't necessarily need to have many API routes.</p><p>
 We can just use simple functions.</p><p>
 Now, since user crowd actions are something that we do in almost every single video to quickly get us started on it, you can find the complete user dot actions.</p><p>
 The as file in the read me of this video copy it and paste it right here.</p><p>
 You'll notice that we have four separate functions delete user update user read user or get user by D and create user.</p><p>
 That's it.</p><p>
 Now, before I explain all of these in detail, you can see it through missing some dependencies to this file, such as the handle error function coming from utils as well as some of the type properties right here.</p><p>
 Navigate over to the read me and override the complete utils dot TS file.</p><p>
 Here, you'll notice that we have our handle error, which is a very simple function that takes in an error and then simply consul logs it appropriately so we know where is it coming from.</p><p>
 There are also some additional things like different loaders, string that modifies the base to base 64 and functions that form our URL query.</p><p>
 As with the user actions file, you can also notice that we're missing some types.</p><p>
 So let's also get access to those types to create the types go right here to the root of your directory, create a new folder called type.</p><p>
 Or we can even call it types.</p><p>
 And within it create a new index dot d dot TS.</p><p>
 In the read me down below, you can also find full declarations of types that will use across the application.</p><p>
 For example, here we have create user programs indicating what we need to pass into the function that will then create a user.</p><p>
 If you want to, you can review these.</p><p>
 But as with any of the files that I provide to you, I will explain them in detail as soon as you add them.</p><p>
 So now our user dot actions TS file is no longer complaining and we can check it out and see exactly what it does.</p><p>
 There are four different server actions create, read, update, and delete.</p><p>
 Let's look into create first.</p><p>
 Since we're using a serverless architecture, we have to call the function that they created in the mom goose dot TS file a bit before.</p><p>
 The one that catches the connection to the database.</p><p>
 And we have to call it on every single call.</p><p>
 Because remember, the connection to the database doesn't persist.</p><p>
 Rather, we have to make another request every time that we want to get something or do something in this case create a user.</p><p>
 But thankfully, we're cashing our connection.</p><p>
 So the next time we try to create a user, it will already know what it needs to do.</p><p>
 And it won't waste any time actually connecting it.</p><p>
 So how are we connecting a user? Well, we simply say const new user and we await a call to the user model where we call the dot create method on it that creates a new document to which we pass the user data coming from the front end.</p><p>
 And what data will it be? It will have a clear ID, email, username, first name, last name, and a photo.</p><p>
 Similarly, we can read the user details like that by using the model and then the method that find one on it where we find one by the clear ID where it's equal to the user ID.</p><p>
 If we don't find it, we throw an error else we return the user data.</p><p>
 The update is very similar as well.</p><p>
 We find one and update the user and we return it back.</p><p>
 And delete is even simpler.</p><p>
 We find a user.</p><p>
 If there is no user to delete we throw an error else we delete a user and send it back.</p><p>
 This is it.</p><p>
 These are the crowd operations for our users.</p><p>
 So now let's go into a bit more detail into the most important function here, which is the one that creates a user.</p><p>
 Well, you might be wondering why do we have to create a database user if clerk is already doing that for us.</p><p>
 Here we have our user, right? Well, yes, but at the same time, no, we have to have access to the user in the database as well.</p><p>
 We have to know has that user created any images.</p><p>
 We have to make references from the user to these images.</p><p>
 And how do we do that? Well, we have to sync the data between a clerk user and our newly created database user.</p><p>
 And we'll do that by using the concept known as webhooks.</p><p>
 A webhook is a concept where when something happens an event is triggered.</p><p>
 In our case, what will trigger an event? Well, in our case, clerk will trigger an event once a user signs up with a new clerk account.</p><p>
 Then it will make a request with a payload containing all of that juicy clerk user data such as the username, first name, last name, hash password and more.</p><p>
 Then it will send that data over to event processing directly to our database so that we can then create a new user within our database and sync those users up.</p><p>
 So let me show you how to connect clerk with webhooks to our MongoDB database.</p><p>
 The easiest way in which we can do that will require us to immediately deploy our application right now.</p><p>
 It's weird, right? We don't yet have a lot of stuff to show, but we need to deploy it so that we can expose our application endpoints to the internet so that once we create a new clerk user, it can ping that endpoint and therefore automatically create a new user in our database.</p><p>
 So let's get our application deployed.</p><p>
 We can do that by going over to GitHub and then entering our repository name.</p><p>
 We can leave it to public for now and click create repository.</p><p>
 Make sure to not tick add or read me because here we have some useful information.</p><p>
 Open up your terminal.</p><p>
 Let's stop it from running so we have more space right here.</p><p>
 And before we deploy it, we just have to make sure that our app works.</p><p>
 So in this case, it looks like we have to install an additional package called GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So we can run GitHub.</p><p>
 So let's give it some time.</p><p>
 And I'll be right back.</p><p>
 And there we go.</p><p>
 In about a minute, our project got deployed.</p><p>
 So you can click right here.</p><p>
 And it will lead you to your new deployed project now on the internet.</p><p>
 And you can see Clerk automatically opens up.</p><p>
 We can see that it is under our new domain.</p><p>
 And we can now sign in as we usually would.</p><p>
 And we are in.</p><p>
 Now we don't necessarily need this page right here.</p><p>
 But what we do need is the URL of our deployed application.</p><p>
 So let's copy it or save it somewhere for later because we'll need it.</p><p>
 Once you save it, we can open up a documentation page for Sinking Clerk data to your backend with Webhooks.</p><p>
 You can find this page just by googling it.</p><p>
 And just by reading the title, it seems like this is exactly what we want to do.</p><p>
 A common set of four applications that involves a frontend for customers to interact with a backend that includes a database.</p><p>
 Since authentication and user management habits on Clerk's side, data eventually needs to reach the applications backend.</p><p>
 And the recommended way to sync that data is via Webhooks.</p><p>
 So let's figure out how to make it happen.</p><p>
 First, we have to go to our Clerk dashboard and navigate to Webhooks.</p><p>
 It is right here on the left side.</p><p>
 Then click Add and point.</p><p>
 And here you can enter the endpoint URL.</p><p>
 In our case, the URL will be the URL of our deployed application.</p><p>
 But we have to point to a specific route.</p><p>
 So that will be forward slash API, forward slash Webhooks, forward slash Clerk.</p><p>
 Make sure to type, make sure to spell this exactly as I have it right here.</p><p>
 API, Webhooks, Clerk.</p><p>
 Because the way it works is Clerk's Webhooks will ping our application upon specific events.</p><p>
 And in our case, the event will have something to do with users.</p><p>
 Specifically, when a user is created, deleted or updated.</p><p>
 And click Create.</p><p>
 There we go.</p><p>
 Now we can keep track of all of the events that are happening.</p><p>
 You'll also notice that there is a signing secret right here.</p><p>
 So make sure to reveal it and copy it.</p><p>
 We then need to go back to our application and add it to our.</p><p>
inv.</p><p>
local by saying Clerk Webhooks Secret.</p><p>
 And we can paste it by saying Webhook underscore Secret is equal to this string right here.</p><p>
 And then if we keep going through the docs, the second step is to add the secret to our.</p><p>
inv, which we have done.</p><p>
 And the third thing is to understand how does the Webhook event payload or in other words data look like? It has a data object that holds the information for the events payload, the object which is always the event, and then the type of the event.</p><p>
 The fourth step is to install the swix package.</p><p>
 This is a package that verifies the Webhooks signature, making it easy to verify the authenticity of the Webhook event.</p><p>
 So we simply need to run mpm install swix, which we can do by running this command in our terminal.</p><p>
 And then we have to create the end point of our application.</p><p>
 We can copy it here directly from the docs, but in the case of our application, I will provide you with a complete Webhook file in the readmedown below.</p><p>
 So copy it from there, go back to our code, and create a new file under app, new folder of API, another folder within it, of Webhooks, another folder within it, of clerk, and then within clerk, create a new file called route dot ts.</p><p>
 This is our first API route, and within here you can paste the complete file.</p><p>
 That's going to look something like this.</p><p>
 It feels like there's a lot happening over 117 lines, but let me show you what is truly happening.</p><p>
 First, we're importing the same user actions that we have created within lib, actions, user actions, and it looks like it's having trouble accessing this file.</p><p>
 But if you simply remove something from it, and then manually go to the user actions, you'll notice that now it's not going to complain.</p><p>
 The way that this Webhook file works is that it's always accepting a Webhook based off of the Webhook secret coming from clerk.</p><p>
 It gets all the headers, the signatures to ensure that we are the real center of the hook, and then we are looking for different types of events.</p><p>
 If the event is user created, then we want to create the user within the database.</p><p>
 If the event type is updated, then we want to update, and if the event type is deleted, then we want to delete it.</p><p>
 That is more or less it.</p><p>
 And of course, we'll dive deeper into each one of these three if statements to figure out exactly what's happening in there.</p><p>
 But for now, let's just ensure that they can actually get cold, and we can do that by following the last step of our documentation here, which is to add our endpoint to middleware.</p><p>
 So let's see what they're doing here, adding public routes.</p><p>
 So navigating over to our middleware.</p><p>
ts file, we can specify public routes, which is an array, where we can say forward slash API, forward slash Webhooks, forward slash clerk.</p><p>
 This means that we're allowing public access to this API endpoint, so clerk can ping it with new events.</p><p>
 So let's test it out by publishing all of this new code to production.</p><p>
 We can do that by opening up the terminal, clearing it, and running gitad.</p><p>
gitcommit.</p><p>
m, implement clerk webhooks, git push.</p><p>
 This will push it directly over to GitHub, which means that Versel will also take notes and start building it.</p><p>
 Now, keep in mind, we added one additional environment variable.</p><p>
 So we have to make sure that it gets added to the final version of the application as well.</p><p>
 So let's go over to settings and then environment variables.</p><p>
 And let's add the secret key going back to our dot in V, that is this one right here, Webhooks secret, and let's paste it and click save.</p><p>
 Now, an important note is that whenever you add a new environment variable, you have to redeploy your latest commit, so it actually recognizes it.</p><p>
 So let's give it a minute until our latest deployment is building and then I'll be right back.</p><p>
 There we go, the latest version of our app has been built in a minute and eight seconds.</p><p>
 And now we can close all of the files besides the local host 3000.</p><p>
 So we're not going to be doing testing on the live deployed version rather local host, which right now is unreachable.</p><p>
 So let's go back over to the code and run mpm run dev to run our application on local host 3000.</p><p>
 There we go, we're back online.</p><p>
 Now, before we go ahead and test anything related to what we've just done, let me ensure to explain it all in detail.</p><p>
 So the way this works is as soon as we create a new clerk user, let me sign out for now so I can show you how that works.</p><p>
 As soon as the new user is created, clerk will ping our new endpoint that we created, which is this one right here, and tell it.</p><p>
 Hey, there's a new event and it's a fake type user dot created.</p><p>
 Let's see what do we do then.</p><p>
 All of the information about that user is saved within event data, things such as ID, email addresses, image URL, first name, last name, user name, and more.</p><p>
 We extract all of that data and set it to a new user object.</p><p>
 Now, once we have a complete user object, we call create user, but what is this? This is the server action that we have created before that takes in all of that user data and then creates a new user in our database.</p><p>
 Then once we do that, we simply merge the clerk ID with our own user ID.</p><p>
 And return back a message saying, okay, similar things happen for update and delete as well.</p><p>
 If the event is user updated, we simply call the update user server action.</p><p>
 And if it is delete, we simply delete it.</p><p>
 So what do you say that we give it a shot? Let's try to continue with Google on our local host and see what happens.</p><p>
 Make sure to choose a new account that you haven't used before.</p><p>
 And of course, you'll need to choose a different username, such as the real JSM and press continue.</p><p>
 Now, this works exactly as it worked before.</p><p>
 We are back here.</p><p>
 We have this wonderful looking navigation.</p><p>
 We can see our user information and it all works.</p><p>
 So what did we even do? Well, here's the difference.</p><p>
 Behind the scenes, clerk, this time let our application know, hey, a new user is created.</p><p>
 Please add it to your database as well.</p><p>
 So now, if we check out our MongoDB Atlas database, enter the cluster and go to collections, you should be able to see a user's collection with one object.</p><p>
 Or two, looks like a user has already been testing the application.</p><p>
 But hey, the user that I created is right here.</p><p>
 It has all of the necessary information, such as the first name and last name, picked directly from clerk, as well as a photo, user name, email, clerk ID, and the real object ID.</p><p>
 It works exactly as we imagined.</p><p>
 So this module was interesting.</p><p>
 You learned how to create server actions.</p><p>
 You just create them in a special file that has a use server directive.</p><p>
 And every function that you export from there is called a server action.</p><p>
 From those functions, we have to then every time connect to the database.</p><p>
 But don't fret, we're doing it in an optimized way because we have a cached connection, which we created before.</p><p>
 Then we introduce the concept of webhooks.</p><p>
 So we created and exposed a new route where we gave permission to clerk's webhook to ping us whenever a new event gets created.</p><p>
 Once it does, we figure out the event type and call our server action, which then of course creates the user in the database.</p><p>
 So now we're syncing the color user data with our database user data.</p><p>
 You'll learn about crud, you'll learn about server actions, you'll learn about webhooks.</p><p>
 This one was great.</p><p>
 Next up, now that we finally have a real user within our database, that user can do stuff.</p><p>
 We'll create the ad image form, which will allow our user to add new image transformations.</p><p>
 So let's do that right away.</p><p>
 We can start by heading over to our app, root transformations, specifically the ad type page right here.</p><p>
 And then within here, instead of rendering this ad transformation type page, which we cannot even see at this point because it's being covered by a navbar, we want to render something different.</p><p>
 And that's something will be a header component.</p><p>
 So let's quickly head over to our components and create a new shared component called header dot TSE, run RIFCE right within it, and then imported and return it as the only thing within our ad transformation type page.</p><p>
 That's going to be a simple self-closing header, which we can import from components shared.</p><p>
 Great.</p><p>
 To this header, we can pass to props.</p><p>
 The first one is a title, and we can do something like transformation title.</p><p>
 And the second one is a sub title, like this, which can be a transformation sub title.</p><p>
 There we go.</p><p>
 That's good.</p><p>
 Let's also put it in a new line, so it's easier to see what's happening.</p><p>
 And you can see our header component now is complaining because if we head to it, it's not accepting any props.</p><p>
 So let's fix it.</p><p>
 Let's make it accept a title, as well as a sub title.</p><p>
 And now that I'm checking this out, it might make more sense to spell out sub title with a lowercase T.</p><p>
 It looks just a bit better.</p><p>
 So make sure that it is exactly the same as how you spell it there.</p><p>
 And we can also provide the types where the title will be of a type string.</p><p>
 And the sub title will be optional, and it will also be of a type string.</p><p>
 Now that we have that information, we can wrap everything within a react fragment, which will look something like this.</p><p>
 And then we can return an H2 element that will have a class name equal to H2-Bold, as well as text-dark-600.</p><p>
 And within it, we can render the title.</p><p>
 And also right below the H2, if the sub title exists.</p><p>
 And only if it does, we want to render a p tag that's going to render the sub title.</p><p>
 And we can also give it a class name, off something like p-16-regular, and empty of four for margin top to divide it a bit from the H2.</p><p>
 And that is our header.</p><p>
 Now why is nothing being shown on the screen? Well, that's because we are on our homepage.</p><p>
 So if we navigate to a specific transformation, like imagery store, you can just start seeing something barely here.</p><p>
 Let's ensure we're in the right one.</p><p>
 We're on the add and then restore.</p><p>
 Yes, this is looking good.</p><p>
 But as you can see, our navbar is hiding the header.</p><p>
 So to fix it, we might need to see what's happening not within the header, but within the root layout.</p><p>
 Here we have a root container class and a wrapper class, which should provide us some space for the header to show below the navbar.</p><p>
 So let's navigate over to our globals.</p><p>
css and right here below the auth, let's also add the root and the container classes.</p><p>
 We can start with a dot root where we can apply a couple of tailwind properties, a flex, min-h-screen, so it takes the full height of the screen, W-full for full width, flex-column, so elements appear one below another, BG-white and on large devices flex-row.</p><p>
 We can also create the root container, so that root-container, and here we can apply the margin top of 16, which will provide the necessary space for it to show, flex-off-one, overflow of auto, padding Y of 8.</p><p>
 On large devices, margin top of 0, because there we don't have the navbar, and on large devices max-h-screen, as well as on large devices padding Y of 10.</p><p>
 If we now save this, you can see that the header appears properly.</p><p>
 Now we can go back to our header and we can see exactly what we're showing, the transformation title and the transformation subtitle.</p><p>
 But the whole goal of this page is for it to display things dynamically.</p><p>
 So if we are on the generative fill, we want to say something about generative fill, same thing for object-removed, recolour, and so on.</p><p>
 So how will we make that happen? Well, to make it happen, we can make use of the perams that we have here.</p><p>
 You can notice that right now we are under the route add and then restore.</p><p>
 And if you remember before, I mentioned that whatever you put in these square brackets, you will have access to within the props.</p><p>
 So specifically in this case, it's the type.</p><p>
 So what we can do in this case is these structure the perams, and then from the perams, these structure the type.</p><p>
 And of course, we have to ensure to specify its type, which will be of a search peram props.</p><p>
 And now that we know on which type we are, for example, generative fill, we want to figure out what to show in the header.</p><p>
 And for that, we can import transformation types coming from add-forward slash constants.</p><p>
 This constants file is a simple object that contains the type that we have in our URL.</p><p>
 And based off of the type, it provides us with a title and a subtitle, as well as the icon.</p><p>
 So it will be very easy to show what we need to show here, simply by taking it out, out of that object, by saying const transformation.</p><p>
 And it's equal to transformation types.</p><p>
 And then we enter that specific type.</p><p>
 For example, if we are under restore, we can go into the transformation types, restore, and now we can grab the title and the subtitle.</p><p>
 So let's say right here, that's going to be a transformation dot title.</p><p>
 And similarly, we can call the transformation dot subtitle.</p><p>
 Back in the constants, I'm using the capital T.</p><p>
 So let's also modify the capital T right here.</p><p>
 And now you can see that we get a nice header that says restore image, refine images by removing noise and imperfections.</p><p>
 And we can go to generative fill, and that also changes the header, changing the title and the subtitle.</p><p>
 This works incredibly well.</p><p>
 Not to mention that it also works on our desktop devices.</p><p>
 So if we go here, you can see home, images store, generative fill, object remove, object recolour, and background remove as well.</p><p>
 Now that we have the header, let's focus on a component that is much more important that also has to be shown on this page.</p><p>
 And that is the form.</p><p>
 So right next to the header within our shared components, we can create a new file called transformation, form dot TSX, inside of which we can run RAFC.</p><p>
 And then we can go back to the page, wrap everything in a react fragment, put the header on top right here, and then immediately after render the transformation, form coming from shared transformation form.</p><p>
 Immediately if you reload, you should be able to see transformation form right here.</p><p>
 So to get started with building our transformation form, we'll check out the docs coming directly from Shats Yen.</p><p>
 They say react hook form, building forms with react hook form and Zod.</p><p>
 Forms are tricky.</p><p>
 They are one of the most common things you'll build in a web application, but also one of the most complex.</p><p>
 Well design forms are well structured, easy to use in Navigate, accessible, have support for client and server side validation, and well styled and consistent.</p><p>
 So in this guide, we'll take a look at building forms with react hook form and Zod.</p><p>
 In this case, we're going to use the form component as the wrapper.</p><p>
 We'll use the form field for an individual field and a library called Zod for form validation.</p><p>
 So let's scroll down a bit and check out the installation process.</p><p>
 First we have to add the form.</p><p>
 So we can copy the installation command, go to the code, split it and add it.</p><p>
 It is as simple as that.</p><p>
 Next we can navigate over to the transformation form and we can import what they say we need to import.</p><p>
 In this case that is Z from Zod and then a form schema.</p><p>
 So to get started with this, we can just import everything from the form schema.</p><p>
 We can just import everything from the first step of the docs and just paste it right here at the top.</p><p>
 First of all, it's a used client component because it has to manage keyboard and key press and submit events.</p><p>
 And then we import Z from Zod and we define our form schema.</p><p>
 And then below the import, we have the form schema for now just referring to the username later on we're going to update it.</p><p>
 Let's go to the second step where we have to import the Zod resolver and the use form right here at the top.</p><p>
 And then we can define the form and define a submit handler which we can do right here at the top of our component.</p><p>
 After that, we can build out our form by first importing the input, all of the things from the form and the button.</p><p>
 So right here at the top, we can paste all of those imports and you can notice that we haven't yet installed an input from Shats Yen.</p><p>
 So we can do that by saying add input right here.</p><p>
 After, we actually render out the entire form.</p><p>
 So let's copy the form and paste it right here as the return of our transformation form component.</p><p>
 And let's make sure to indent it properly.</p><p>
 There we go and that's it.</p><p>
 You have a fully accessible form.</p><p>
 So to verify that truly to be the case, we can see that this looks wonderful.</p><p>
 We have a username input with some helper fields and a submit button with a complete validation right out of the box with Shats Yen.</p><p>
 But now we want to turn this very simple form that has one single username field into something much more complex.</p><p>
 So let's get started with the validation where we can define what kind of fields or inputs we want to have.</p><p>
 Well, we can start with a title field of a type z dot string.</p><p>
 Then we can have the aspect ratio of our image, which will be of a type z dot string and then dot optional.</p><p>
 We can also do something like color, which will also be a z dot string in that optional, as well as a prompt, which will also be a z string optional.</p><p>
 And finally, we need a public ID, which will be a z dot string, but not optional.</p><p>
 Now we have all of our fields and we also have to define the default values.</p><p>
 So right here at the top of our component, let's say const initial values is equal to.</p><p>
 And now in case we're doing the editing on a specific image, not creating, then we might immediately have some data from before.</p><p>
 In that case, we can check if data exists and if action is triple equal to update with a capital U, then we can define an object containing some default values.</p><p>
 Else, we can simply set it to true default values, which are coming from constants.</p><p>
 That's going to look something like this where we have all of these empty fields, title aspect ratio, color, prompt and public ID.</p><p>
 Which we can copy for now.</p><p>
 And we can use them as the default values in case if we already have some values.</p><p>
 But in this case, we will already have access to these pieces of data.</p><p>
 So let's hold the Windows key or the command or the option key to select multiple cursors.</p><p>
 And then you can keep holding it to move through these words while you're still holding it hold a shift key to select it.</p><p>
 Command or control C to copy it, remove the empty string like this and then say data question mark dot and then paste it.</p><p>
 This way we immediately can fill in all the data or if that keyboard shortcut combination seemed like magic to you.</p><p>
 In that case, you can simply type out what I have right here.</p><p>
 So this will only populate the data of the form in case we're updating the already existing image.</p><p>
 But now I'm sure you can notice the error we have on the right side.</p><p>
 And that is that the data and action doesn't exist.</p><p>
 So what do you say that we get it through props? We can say action and data which by default can be said to now is of a type transformation form props.</p><p>
 There we go.</p><p>
 And now it's no longer complaining.</p><p>
 Now when we're defining the default values of our form, we can simply set the default values to be equal to initial values.</p><p>
 And later on once we call this form component, you'll see in which cases will pass the data in.</p><p>
 Of course, if you're creating the image for the first time, it's going to be empty.</p><p>
 Therefore, the values will be empty.</p><p>
 But if we're editing, then it will pull the values from the data.</p><p>
 And finally, for now in the submit handler, let's simply consulog the values we're getting back.</p><p>
 Now going back to where we're calling this transformation form, let's give it some props.</p><p>
 Let's give it an action which in this case will be equal to add like this.</p><p>
 Because we're for the first time adding or creating an image, not editing it.</p><p>
 Because we are in which page, the add page.</p><p>
 Let's also give it a user ID.</p><p>
 So we know which user is creating this specific image.</p><p>
 So we can say user ID is equal to.</p><p>
 And now we have to figure out where this user is coming from.</p><p>
 And with clerk, it's quite easy to get access to the currently logged in user.</p><p>
 We can do that right at the top by saying const, user ID, destructuring it from auth, which you import from clerk next to us and call as a function.</p><p>
 And instead of passing it directly right here, which would maybe make sense, user ID match with the user ID, in this case, we need to pass a real user ID, the underscore ID coming from the user in the database, not the clerk ID.</p><p>
 So what we need to do first is say const user is equal to a wait get user by ID, which is coming from user actions.</p><p>
 And then we pass the user ID like this.</p><p>
 And of course, we have to turn the entire function into an async function since this is an await call.</p><p>
 And this user ID sometimes might be of a type now.</p><p>
 And now that we have the user, we can pass the user dot underscore ID right here, which is the real ID of the user in the database.</p><p>
 We can also pass the type of the transformation we're doing in this case, that's the transformation dot type as transformation type key, which is just the type.</p><p>
 And we can pass the credit balance, which is equal to user dot credit balance.</p><p>
 Now we're passing everything we need to to the transformation form.</p><p>
 You can see that we no longer have an error, but the question is, why is TypeScript complaining right here that the user ID could possibly be null? Well, the only thing we have to do to resolve it is say something like if there is no user ID, in that case, just do a redirect coming from next navigation to forward slash sign in like this.</p><p>
 There we go.</p><p>
 Now everybody is happy.</p><p>
 And we're now calling our transformation form from the ad page.</p><p>
 So finally, we can close that page.</p><p>
 We can close the header and we can focus only on implementing our form.</p><p>
 Currently, we have one form field right here, but let's delete it because we'll create our own.</p><p>
 We can leave the form empty for now to save us some time and copying from Shatsy and stock.</p><p>
 I'm going to provide you with a complete custom field component, which you can get from the read me down below, copy it and then paste it over to components, shared and creating new file called custom field dot TSX and paste it right here.</p><p>
 It's just a 43 line long file where we're using Shatsy and form field.</p><p>
 But then we're also allowing for some modifications by passing some special props into it, such as allowing us to dynamically change its name, class names, labels and more.</p><p>
 You can notice that it's complaining that it cannot get the form schema from the transformation form.</p><p>
 So what we have to do is go back to the form and from here, we can also just say export const form schema.</p><p>
 So now we can use it both within the transformation form as well as within the custom input.</p><p>
 Great.</p><p>
 Now within the transformation form, we can use that custom input we created.</p><p>
 So let's use it for the first time ever by creating a custom field or rendering it as a self closing component.</p><p>
 That has to have a control equal to form dot control so we can actually control that field.</p><p>
 It can have a name equal to title, a form label equal to image title, a class name equal to W dash full, as well as a render property so we can say what it will render.</p><p>
 And here we can destructure the field from the values we're getting and we can automatically return an input property, self closing component to which we can spread all of the props coming from the field and we can also pass it a class name of input dash field.</p><p>
 So now if we save it, you can see a wonderful looking theme matching image input field.</p><p>
 Next, we want to create a similar custom field for choosing the aspect ratio and that won't be an input rather it will be a select.</p><p>
 This is how it looks like you click it and then you get a couple of options to choose from.</p><p>
 So we first need to add select to our code by pasting this command mpx chatc and you I latest add select and then we can figure out its usage.</p><p>
 We can from the docs page we can copy all of the imports right here to the top and we can copy the actual use.</p><p>
 So let's go down and let's paste it right below our custom field if and only if the type is triple equal to fill.</p><p>
 Then we want to display this custom input or custom field like this that will have a render property where we get the field.</p><p>
 It will return what we copied from Shats Yen which is this entire select field.</p><p>
 So now if we save it, it will look something like this and it looks like it cannot find this type.</p><p>
 The type in this case should come from props as we're passing it into this component.</p><p>
 So in the props, we can also declare the user ID which we're passing the type and the credit balance.</p><p>
 We can also get access to the current transformation type we're doing by saying const transformation type is equal to transformation types coming from constants where we tap into that specific type.</p><p>
 That looks something like this.</p><p>
 It can be a restore, remove but in this case we're looking for fill.</p><p>
 Also, we need two new states.</p><p>
 Soon enough we'll implement the image upload.</p><p>
 So we can say use state, use state snippet called image set image and at the start it will be set to data.</p><p>
 This will contain all of the information about our image.</p><p>
 Of course, it starts with a lowercase letter and don't forget to import use state from React.</p><p>
 Alongside image, we also want to keep track of the new transformation.</p><p>
 So what are we doing with that image? And we can create a new use state snippet called new transformation.</p><p>
 And at the start it's going to be equal to now.</p><p>
 And just to satisfy a tap script, we can also specify that it will be of a type, transformations like this or null.</p><p>
 But instead of using the double real logical or we are going to use the single or which is for type script.</p><p>
 There we go.</p><p>
 So now that that is out of the way, let's also just create a simple function to handle our select called const on select field handler.</p><p>
 Pretty descriptive, right? It's going to take in the value of a type string as well as the on change field, which will be of a type a function that accepts a value of string and then returns void meaning nothing.</p><p>
 And then we can open up the function block here and fill it in later.</p><p>
 For now, we just need it so we can use it within the select field below.</p><p>
 Now with this custom field, we cannot yet see it because we're not on the fill page.</p><p>
 But now if we navigate over to generative fill, then we should be able to see the aspect ratio as well, which we cannot because we're not passing all of the necessary fields to the custom field.</p><p>
 So let's pass it a control equal to form that control.</p><p>
 Let's pass it a name equal to aspect ratio.</p><p>
 Let's give it a form label equal to aspect ratio.</p><p>
 And let's give it a class name equal to W dash full.</p><p>
 If we save it, we can see a nice looking picker that's not choosing the aspect ratio rather the light and dark theme.</p><p>
 That's because we are yet to modify this default select coming from Shatsy and stocks.</p><p>
 And we can do that by passing our select handler to the select.</p><p>
 So on value change will be equal to we get in the value and then we call the on select field handler to which we pass the value and the field on change.</p><p>
 Then we have the select trigger which in this case will have a class name equal to select dash field.</p><p>
 And it will render the select value with a placeholder of select size.</p><p>
 Now within the select content, we want to remove all the selected items and render a dynamic block of code getting the object that keys of the aspect ratio options.</p><p>
 Being dynamically imported from constants.</p><p>
 Let's check it out what this aspect ratio options is.</p><p>
 As you can see, it is an object that has different properties one by one, which is a square, typical nine by 16 and three by four.</p><p>
 And each one has widths, heights, labels and more.</p><p>
 So we first want to extract the keys, which are the names of these options and then say dot map where we get each individual key like this.</p><p>
 And then for each key, we automatically return a select item.</p><p>
 That select item has a key equal to key, a value equal to key and a class name equal to select dash item.</p><p>
 Finally, within it, we can show dynamic block aspect ratio options and then we get into a specific option key as aspect ratio key like this.</p><p>
 And of course, let's spell it properly aspect ratio key and then we say dot label to only pick the label.</p><p>
 In this case, it looks like TypeScript is complaining a bit because it cannot find name aspect ratio key.</p><p>
 And that's because if you copy that name and paste it in the search, you'll see that we are referring to that aspect ratio key right here within utils, but we're never exporting it.</p><p>
 So right here, we can say export aspect ratio key and then here we can import it from lib utils and now we're good.</p><p>
 So let's see what we just created.</p><p>
 We have the image title and then we have the aspect ratio, which is a nice select field where we can see all of these different types of ratios we can select such as square, portrait and phone portrait.</p><p>
 The reason why we have the title as well is we have it here under label and we're rendering this label.</p><p>
 This is not the first time that we have some pieces of data or the text within constants and then we simply use it within our code.</p><p>
 It is so much easier like this.</p><p>
 Imagine if here we had to type standard portrait, phone portrait and then render different select items.</p><p>
 No, we simply map over them coming from constants.</p><p>
 Now let's move on to some other custom fields.</p><p>
 We have this one that only shows if we are on type fill, we have the title which always shows, but now we're going to have two more which only show on some of these pages right here.</p><p>
 So let's start creating them by saying if in parentheses type is triple equal to remove or type triple equal to recolor.</p><p>
 Only then render a div that will have a class name equal to prompt dash field.</p><p>
 So only if we're doing a special action of removing objects from images, then we want to know what we want to remove and recolor to tell it to which color we want to change it to.</p><p>
 So in those cases we want to render a prompt field that's going to be a custom field self closing component with a control equal to form that control a name equal to prompt a form label equal to if type is triple equal to remove.</p><p>
 It will say object to remove else it will say object to recolor that is our form label.</p><p>
 And right here, Tabscript gave us an interesting warning saying this comparison seems to be unintentional because the types recolor and remove have no overlap.</p><p>
 But who said that the type will only be recolor.</p><p>
 Let's see what type is it referring to.</p><p>
 It's referring to this type right here coming from our transformation form.</p><p>
 And let's see what that type can be.</p><p>
 Well, it can be any of the transformation type keys.</p><p>
 It can be restore fill remove recolor.</p><p>
 So why does our Tabscript think that it is only going to be recolor? That's interesting.</p><p>
 We can leave it for now and then see why is it saying that later on.</p><p>
 For now, let's move on to the class name, which will be W dash full.</p><p>
 And let's render the actual field as before we destructure the field data.</p><p>
 And then we immediately return in this case an input.</p><p>
 But of course, it's a chat scene input like this.</p><p>
 So if we save it, we cannot see anything here.</p><p>
 But if we move over to object remove, we cannot see it there either but in recolor, we see it right here.</p><p>
 Object to recolor.</p><p>
 That's good.</p><p>
 Oh, I think I know what it is.</p><p>
 I think Tabscript just saved our ass.</p><p>
 That's because if I go to object remove, you can indeed see that there's no field.</p><p>
 And Tabscript is saying, hey, but this will never be removed.</p><p>
 It's always going to be recolor.</p><p>
 Why? Because I said if type is recolor, then show this.</p><p>
 What I meant to say is if type is remove or type is recolor.</p><p>
 So I need to merge these two ifs together.</p><p>
 So now it shows both an object to remove and object to recolor.</p><p>
 Great.</p><p>
 Now to this input, we can give a value of field dot value.</p><p>
 We can give it a class name equal to input dash field.</p><p>
 We can also give it an on change property equal to where we get the event.</p><p>
 And then we call on input change handler to which we want to pass first of all the string of the field we're changing, such as prompt.</p><p>
 Then we want to pass it the e target value.</p><p>
 So it knows the data that we're trying to type.</p><p>
 The type are we trying to remove an element from an image or are we changing the color and then the field dot on change itself.</p><p>
 So we can create this on change input handler right here at the top.</p><p>
 Const on input change handler is equal to where we get the field name of a type string.</p><p>
 We get the value of a type string, a type of a type string and an on change field of a type function that takes in the value of a type string and returns void.</p><p>
 And finally we open up that function block.</p><p>
 So now we have the on input change handler.</p><p>
 And we have our object to remove which is just a regular input.</p><p>
 And it seems like if I reload the page, it doesn't allow me to type into it.</p><p>
 But in the image title I can, that's because we'll manually modify the way to update this input within this function.</p><p>
 Before we do that, let's add another input field.</p><p>
 This one specifically will only show if we're trying to recall or something.</p><p>
 So let's go all the way down below this second closing statement.</p><p>
 And let's say if type is triple equal to recolor, then render a custom field that has a control equal to form dot control.</p><p>
 And let's switch it over to recolor so we can actually see it.</p><p>
 There we go.</p><p>
 We cannot see it yet because we have to pass the name equal to color.</p><p>
 The form label equal to replacement color, the class name equal to W dash full.</p><p>
 And then finally what we want to render, which we will get access to the field.</p><p>
 And we will return the self closing input tag just like so.</p><p>
 So now if we save it, we have the replacement color right here.</p><p>
 This input will have almost exactly the same fields as the above one does so we can copy them.</p><p>
 Pays them here and just change the prompt right here to color.</p><p>
 And instead of type, we can say recolor right here as well.</p><p>
 Finally, let's not forget a submit button right here above our closing form.</p><p>
 So we can say button and it's going to say submit and it will have a type equal to submit.</p><p>
 There we go.</p><p>
 That's more like it.</p><p>
 But let's make that button suit our design a bit better by expanding it right here so we can pass it a couple more props.</p><p>
 Let's also give it a class name equal to submit dash button and capitalize.</p><p>
 There we go.</p><p>
 That's much more like it.</p><p>
 And let's make it disabled in case we're currently submitting.</p><p>
 And to be able to know for currently submitting, we have to add a new use state field at the top.</p><p>
 So let's add a new use state snippet is submitting set is submitting at the start set to false.</p><p>
 Let's also add a couple of other use states, which we will definitely need such as use state of is transforming.</p><p>
 So are we currently doing something with the image at the start set to false as well.</p><p>
 And let's also create a new use state of transformation config set transformation config at the start equal to config.</p><p>
 And that config will be coming through props and by default, it can be set to now.</p><p>
 Now that we have those fields, we can set the disabled state if we currently are submitting.</p><p>
 So our users don't press it multiple times.</p><p>
 There we go.</p><p>
 That's great.</p><p>
 And let's also add a button to apply a transformation.</p><p>
 So this is a button to submit the transformation.</p><p>
 But above it, we need to have one to apply the desire transformation.</p><p>
 So we can create a div to wrap those two buttons in with a class name of flex flex dash call and a gap of four within here.</p><p>
 We can put this button right above to place it within this div and we can also duplicate it.</p><p>
 So the first one will not be a type submit rather just a type button with a class name of submit button and capitalize disabled.</p><p>
 If we are is transforming or if new transformation is triple equal to now and on click, we of course want to call the on transform handler, which will be the actual function that handles the logic of doing something to the image.</p><p>
 So we can say const on transform handler, make it look just like this for now.</p><p>
 And that button can say something like if is transforming, then it can say transforming dot dot else, it can say apply transformation.</p><p>
 And the second button is not going to say submit if we are submitting.</p><p>
 So if it's submitting, it will say submitting else, it will say save image.</p><p>
 Great.</p><p>
 So now let's save it to see how does it look like.</p><p>
 And would you look at that? We now have a real form where we can apply different transformations, enter the images, recolor them, do replacement color.</p><p>
 And that's only for the recolor.</p><p>
 But if you go to image restore, it's going to look completely different as we don't need all of these additional fields for background remove, we will have the image title.</p><p>
 And for object remove, we also have the object to remove.</p><p>
 So it's fully dynamic and you have the action fields.</p><p>
 Now let's also apply a bit of a margin here to divide the form from the header.</p><p>
 We can do that within the ad page.</p><p>
 So that will be page.</p><p>
tsxoffad right here where we have the header.</p><p>
 So we can wrap the transformation form within a new section like this.</p><p>
 Put this a bit up, invented and give it a class name equal to.</p><p>
 Margin top of 10.</p><p>
 There we go.</p><p>
 That's more like it.</p><p>
 And finally, the last thing that is remaining is we have to handle these three or four functions we have.</p><p>
 What happens on select? What happens on input change? What happens on transform handler? And finally, what happens on submit? So let's go ahead and handle these three first.</p><p>
 So we know what happens on input clicks and then we will handle on subit last because it allows us to take all of those changes from those inputs and turn it into something great.</p><p>
 Starting with the on select field handler, we can first move into where we have access to it.</p><p>
 I believe it's within let's let me think about it.</p><p>
 Maybe fill.</p><p>
 Yes, because we have to figure out which aspect ratio of the image we want.</p><p>
 So let's implement the functionality for the on select field changer.</p><p>
 First, we need to get the image size by saying const image size is equal to aspect ratio options.</p><p>
 And then we get the specific value that we have selected, which is going to look something like this.</p><p>
 And we can use typescript as aspect ratio key.</p><p>
 There we go.</p><p>
 So there we get the size.</p><p>
 Then we want to set the image by calling set image where we get previous state.</p><p>
 So preve state.</p><p>
 And we want to immediately return something for immediate return.</p><p>
 You need to wrap it in parentheses and then put an object within it where we spread the previous state non intentionally.</p><p>
 We specify the aspect ratio, which will be equal to image size dot aspect ratio.</p><p>
 We specify the width, which will be equal to image size that width and height, which is image size that height.</p><p>
 And for now, we can set the preve state as any.</p><p>
 Finally, once we have the image, we can set new transformation to be equal to transformation type dot.</p><p>
 Because now we know the configuration of the transformation we're doing.</p><p>
 And then finally, we can return the on change field with the modified value.</p><p>
 So this is how we're handling those inputs within our form.</p><p>
 So now we can actually select it and it will be selected.</p><p>
 And later on, we'll be able to apply a transformation.</p><p>
 Now, let's focus on the input handler.</p><p>
 So we can select a new input, which is within object remove.</p><p>
 There we go.</p><p>
 And let's modify it by first using a function known as debounce.</p><p>
 So right here, we can say debounce, which can be imported from lib utils.</p><p>
 And to it, we'll pass an empty callback function.</p><p>
 And as the second parameter, a debounce value of 1000 milliseconds or one second.</p><p>
 If you don't know what a debounce input is, it looks something like this, where it waits a specific amount of time.</p><p>
 And then submits your entry, whereas with the regular input, every single keystroke will be registered.</p><p>
 And imagine if you sent all of those requests to your back end or to the cloud and our API to actually make multiple variables.</p><p>
 It would put stress on your server and it would just cause too many unnecessary requests because you weren't even finished with the typing.</p><p>
 So that's why we want to implement a one second debounce.</p><p>
 So right within it, we're going to also call the set new transformation to which we're going to get the previous state of a type any.</p><p>
 And then we will immediately return an object where we spread the previous state and modify the type of the transformation to da da da da, prev state, question mark, da and then type like this.</p><p>
 So what we're doing is spreading out the entire state and then tapping into a specific property, a specific type of change like recolor or in this case, remove.</p><p>
 Spreading all of the properties that it has and then if field name is triple equal to prompt, then we return prompt else we return to like this.</p><p>
 And we can modify that to value because if it's a prompt, that's the prompt that you specify.</p><p>
 And I believe the other alternative is the recoloring option.</p><p>
 Finally, we want to return on change field with the value to down one more to go.</p><p>
 This is the transform image handler.</p><p>
 This one will be asynchronous and it will modify a new react field.</p><p>
 This time we won't use a state field as we usually do.</p><p>
 This time we'll use a use transition hook coming from react by saying use transition coming from react and it looks a lot like a typical state where we say const is pending and then start transition like this.</p><p>
 But it's not.</p><p>
 Rather what the use transition react hook does is it lets you update the state without blocking the UI.</p><p>
 So moving down, we can start with set is transforming to true.</p><p>
 Then we can set transformation config to now bear with me.</p><p>
 We want to call a function called deep merge objects coming from lib butils and we want to merge the new transformation with the transformation config.</p><p>
 And this deep merge objects function is a function generated by chat GPT where it essentially merges all of the keys of both of these objects to ensure that all of them end up in a newly created object which we then set to transformation config.</p><p>
 Finally, we can set new transformation to now as we're not doing it anymore and we can start a transition.</p><p>
 So start transition which takes in an asynchronous callback function that looks like this and in there we await the update of the credits.</p><p>
 So later on we'll be able to update the credits as soon as we start a transformation.</p><p>
 Of course we want to charge credits to a specific user ID and we want to take a specific credit fee.</p><p>
 But for now we don't have access to these properties so we can simply comment it out and we'll uncomment it later on.</p><p>
 So I will add a to do right here so we know that we need to return back to it.</p><p>
 So I'm going to say return to update credits but with that we can collapse this on transform handler.</p><p>
 And with that our entire form is now fully functional.</p><p>
 We have different fields that show on different occasions or rather should I say on different pages.</p><p>
 And we are properly handling all the inputs selects but what we're not handling right now is the on submit.</p><p>
 And this is where the magic happens.</p><p>
 So now that we're collecting different things we need to transform the image such as the title, objects to remove, all the other information fields aspect ratios and so on.</p><p>
 We need cloud in areas real superpowers for doing all of these transformations such as restoring our images, doing generative fill, removing objects, recoloring them, removing backgrounds and more.</p><p>
 Because even though our code is great we need to leverage cloud in areas AI feature to make our app come to life.</p><p>
 So let's summarize what we have done up to this point.</p><p>
 You learned how ZOD works, how you can specify different validations on your form.</p><p>
 You learned a bit about how react hook form works by passing specific controls and the rendering different fields on our custom field.</p><p>
 And at the end of the day you learn how to create a completely functional form using chat's yen, ZOD and react hook form.</p><p>
 So now that the structure is set up we can start learning about cloud memory, handle the image upload, which will be the single most important part of our form.</p><p>
 Because right now what can we do just select the image title, change the aspect ratio.</p><p>
 We need to upload our images and then make transformations to them.</p><p>
 So let's do that next.</p><p>
 Learn about cloud memory, learn how we can upload images with it and how to do transformations.</p><p>
 To get started with using cloud memory, click the special link in the description that will allow you to replicate everything you're seeing in the video.</p><p>
 Once you're there, click get started and sign up for an account.</p><p>
 And immediately after the sign up we are on our getting started guide.</p><p>
 If this is not the screen you're seeing but something else, try signing up with a different account.</p><p>
 I noticed that with my previous account I was able to see something completely different after signing up.</p><p>
 So try a different one and you should be able to see this.</p><p>
 So let's get started with setting up our environment.</p><p>
 Here they say to install just cloud memory, but we'll install the next years version of cloud memory.</p><p>
 So we can open up our terminal and run mpm install next dash cloud memory and press enter.</p><p>
 The second step is to get all of our keys and we can get our keys by clicking view credentials and we can copy one by one.</p><p>
 So let's start with the API key and we're going to put it within our ENV local.</p><p>
 So we can create a new section called cloud memory and we can put a cloud memory underscore API underscore key is equal to this key right here.</p><p>
 Next we have the API secret.</p><p>
 So we can say cloud memory underscore API underscore secret.</p><p>
 And finally we also need the cloud name.</p><p>
 So right at the top this can be public so we can say next underscore public underscore cloud memory underscore cloud underscore name and we can paste it right here and close it.</p><p>
 Now we almost have everything we need.</p><p>
 So the first thing we can do is head over to settings right here on bottom right.</p><p>
 Let me zoom it in so you can better see it.</p><p>
 And once you're here you want to navigate over to upload.</p><p>
 Then scroll up the down and click enable unsigned uploading.</p><p>
 If this didn't work for you that could mean that you previously use this account and hit its limits.</p><p>
 So if that is the case simply go ahead and sign out and then sign up within you account.</p><p>
 Now that we have this go to add upload preset.</p><p>
 And in this case start with JSM underscore and then the name of your app.</p><p>
 I'm going to do imagine if I and change the signing mode to unsigned and add a folder of the same name.</p><p>
 As your app in this case imagine if I then head over to media analysis and AI turn on Google auto tagging and set it to somewhere around 0.</p><p>
5 and press save.</p><p>
 You'll see a new upload preset appear right here.</p><p>
 Then you want to go to explore and click on add-ons and you want to turn on two features.</p><p>
 First is the cloudinary AI background removal.</p><p>
 You can click free and you'll be subscribed to a free plan.</p><p>
 There we go it should be blue once it's done.</p><p>
 And we can also select a second one which is Google auto tagging.</p><p>
 And you can also go for the free plan and click agree.</p><p>
 There we go.</p><p>
 So now if you reload and go to add-ons you'll see that you have two of these pro features enabled with a completely free plan.</p><p>
 We'll use the cloudinary AI background removal for removing the background from the image of course and then we'll use Google auto tagging for the descriptive search feature.</p><p>
 That's quite exciting.</p><p>
 So with that in mind you've just learned how to set cloudinary up.</p><p>
 And now we'll use two of their very important components.</p><p>
 The cloudinary upload widget which will allow you to upload images as the name says.</p><p>
 And then the cloudinary image which will allow you to optimize the way that that image is viewed.</p><p>
 So to get started with that let's create a new file under our components shared and let's call it media uploader dot T.</p><p>
S.</p><p>
X.</p><p>
 Within here we'll use a Shatsy and toast.</p><p>
 A toast is this succinct message displayed temporarily.</p><p>
 You click it and you get a notification.</p><p>
 So let's quickly add it to our code by running mpx Shatsy and UI latest add toast.</p><p>
 And then to use it you can run RAFC in the media uploader.</p><p>
 Import the use toast coming from components.</p><p>
 UI use toast.</p><p>
 And we'll get a function that allows us to call that toast on a button click.</p><p>
 We'll use this directly within the cloud in areas upload widget.</p><p>
 So let's talk a bit about that widget and let's talk about using cloudinary with next.</p><p>
 At the start of the segment we installed a package called next cloudinary.</p><p>
 It is a community build solution for using cloudinary in a next.</p><p>
 She has project.</p><p>
 It includes tools like CLD image component, social cards and most importantly an upload widget.</p><p>
 So we have to install it.</p><p>
 We have to add our keys which we have done and then we can immediately use it within our project.</p><p>
 Let's try to find the uploader because that's what we want to do right now.</p><p>
 And if we do a command F for CLD upload we can click right here to navigate to it.</p><p>
 And here we have information directly from next cloudinary that tells us how to get started with a CLD upload widget.</p><p>
 So let's see its usage.</p><p>
 Use the following to generate an unsigned upload widget we can expand it.</p><p>
 And you know what there's not a lot of code so let's go ahead and write it by hand.</p><p>
 Going back to our app we can use this media uploader within the transformation form.</p><p>
 So that's going to be right here specifically we want to put it above our submit button.</p><p>
 So here we have our buttons within this div and right above it we want to put a div that will have a class name equal to media dash uploader dash field within here.</p><p>
 We can render believe it or not yet another custom field.</p><p>
 Yes we will mask cloud in our ease media uploader widget within a custom field so that we immediately get its data within our form.</p><p>
 So let's give it a control equal to form that control.</p><p>
 Let's also give it a name equal to public ID of the image will upload.</p><p>
 Let's give it a class name equal to flex size dash full and flex dash call.</p><p>
 So elements appear one below another.</p><p>
 And finally and most importantly let's run the render where we get that field and we immediately return something and that something is the media uploader component which we just created which we have to import from that slash to that media uploader we can pass a couple of props.</p><p>
 We can pass the on value change equal to field dot on change.</p><p>
 We can pass the set image like this is equal to set image coming from the state.</p><p>
 We can pass the public ID equal to field dot value.</p><p>
 We can pass the image equal to image and we can pass the type equal to type.</p><p>
 Now if we save it this should appear on all transformation pages and there we go you can see the media uploader here which means that now we can navigate over to the media uploader and start working on it.</p><p>
 So let's immediately turn this div into a CLD upload widget.</p><p>
 It won't be a self closing component and it will only have one G this will be coming from next cloud and we can say import CLD upload widget coming from next dash cloud and we need to pass a couple of props to it will pass the upload preset which we created and here the name has to be exactly the same as what you used within your cloud and our dashboard.</p><p>
 So I'm going to say JSM underscore imagine if I if you don't remember it go to settings up pulled presets and see how you named yours.</p><p>
 Then we can provide some extra options such as multiple is false so we only allow for one image upload and then resource type in this case will be just a string of image.</p><p>
 Now we of course have to handle success and failure so let's say on success we're going to call the on upload success handler which is a function which we can declare right here above const on upload success handler.</p><p>
 It will accept the result equal to any and we can just open up a block of code similarly we can create the on error that's going to be on error and it will be on upload error handler and we can just duplicate this function we don't have to pass anything in it in this case and just say on upload error handler.</p><p>
 Later on on success will do some additional things to it of course but for now we can handle the error by showing up a toast element so something like toast where a title of the toast is something went wrong while uploading like that.</p><p>
 We can also give it a description of something like please try again duration of about 5000 milliseconds and a class name equal to error dash toast great we can also duplicate this toast and put it over within the success handler where it will say something like image uploaded successfully in the description we're going to say one.</p><p>
 We're going to say one credit was deducted from your account duration 5000 and success toast great so now that we have the error and success we can move over to what will we show within this upload widget and what we can do with the upload widget is open a dynamic block of code and immediately have a function inside of which we can destructure the open state.</p><p>
 And then we can automatically return something so within here we can return a div that div will have a class name equal to flex flex dash call and a gap of four immediately within we can create an h3 that will have a class name equal to h3 dash bold text dash dark dash 600.</p><p>
 And we can give it a title of original so this is the original image that we're uploading and we can display that image if we have a public ID and this public ID will be coming from props as we're passing it into this component so right here we can accept all of the props that we need such as on value change set image image public ID and the type.</p><p>
 And we can say that all of that is of a type media uploader props and we can define that type right above by saying type media uploader props is equal to an object where we can set the on value change to be equal to a function that accepts a value of a type string and returns void meaning nothing we then have set image which is the object.</p><p>
 And this is a react dot dispatch that's how you specify a type for react set states and the D is capitalized then we have a public ID which is of a type string we have the image of a type any and we have a type of a type string.</p><p>
 Now right here within the image we can check if we have access to a public ID and if we do we can render an empty react fragment and if we don't we can render a div at the start we won't have anything right so here we can say here is the image because we have a public ID and here we can say here is no image and you can see that we get here is no image.</p><p>
 So let's get started with the case where we don't have a public ID yet because this is where we'll actually upload the image so we can give a class name to this div equal to media dash uploader underscore CTA as a call to action to upload the image and on click we can call the open function coming from cloud in a way but in this case it will have to be a call back function that then calls the open look.</p><p>
 So we're looking like this right within that div will create another div which will look like this and it will have a class name equal to media dash uploader underscore CTA dash image and within here we can show kind of a place holder for the image by rendering a next G S image tag importing it from next image that will have a source equal to forward slash assets forward slash icons forward slash slash add that SVG we can give it an all tag of ad image a width of about 24 and a height of about 24 as well and right below the image we can do a p tag that has a class name equal to p dash 14 dash medium that will say click here to upload image and save.</p><p>
 There we go now we have a little plus sign where we can upload the image and this doesn't look the best so we might want to put this p tag below this div containing the image and now it's much better so now you can clearly see that we can upload an image and if you expand this let me show you what you get with cloud in a reason upload image widget out of the box you click here immediately you get this widget where you can upload your own files.</p><p>
 Search from different web addresses turn on the camera Google drive drop box chatter stock get images and so much more let me try on splash to get some free images and let's take this one right here a mountain covered in snow and click next we can select the format and the size you can see that you immediately get different options I'm going to choose medium and I'm going to click upload and as soon as you click upload it will upload it and bring you back to your home page now we didn't get either an error or a success message so let's keep working in our code back here we have to implement the part that happens after we upload the image so in this case let's wrap everything in a div that will have a class name equal to cursor dash pointer overflow dash hidden rounded dash 10 pixels of course in square brackets because we want to make sure that tailwind reads it as a pixel value now right within there will use a special CLD image coming from next cloud in a this image allows you to pass a width which can be equal to now we can dynamically calculate a width by using the get image size coming from lib utils and passing it a type the image and finally the width property the way this works is this is a special utility function that takes our aspect ratio and then returns the dimensions based off of that aspect ratio we can do the same thing for the height by saying height is get image size that's going to look something like this we can also pass it a source equal to public ID the file tag equal to image we can pass its sizes equal to in parentheses max width of 767 px and then close parentheses 100 v w for width and then comma 50 v w we can also give it a placeholder equal to data url as placeholder value like this and let's spell it properly placeholder and this data url will also be coming from utils that basically gets the url off our shimmering effect which means that it will slowly load up until the real image gets there and we can give it a class name equal to media dash uploader underscore CLD image like this now it doesn't seem that our image was recognized though so let's finalize our on success upload right here what will happen once we successfully upload the image well we can set the image to the state so we can say set image where we get a previous state that's of a type any and then we immediately return like this a new object where we spread the previous state we update the public ID with result question mark dot info question mark dot public underscore ID we can update the width to be result info with the height result info height and then a secure url by saying result info secure url and finally we want to call a very important function called on value chain which will take into account that the form changed and then it will update the public ID within our form that will let our application know that the public ID indeed has changed and then we can display this newly uploaded image let's save it and give it a shot I'll try to re upload the image by selecting the size medium and clicking upload and it doesn't look like it got it one thing that we might have missed is adding a use client directive at the top of this file now even though we're not manually using any of the hooks ourselves we are using used toast which is a hook and we're also using the media uploader which of course has to do something with a browser and the user actually uploading the image so this definitely needs to be a use client component it is possible that next year's missed reminding us about the fact that we needed but thankfully I remembered and let's see what happens if you reload and try to re upload that image I'm going to do the same one mountain covered in snow with a pink sky and click upload there we go that's a nice shimmer and immediately we can see the image right here so this is the media uploader component that we were working on and if we try to collapse it a bit you can see that it still looks great even on mobile devices and one thing that I noticed that we're missing is I didn't see the toast letting us know that the image has been uploaded successfully and that's because for the toaster work yes we definitely need to call it like this but we also need to put it right here within our layout dot TSX we need to put this toaster component right below the main so going over to our route layout we can put the toaster right here at the bottom so toaster importing from components UI toaster now if we save it go back and try to re upload you can see that this cloud in our interface looks good on mobile devices as well let's go with this one this time next medium and upload that's looking good original and there we go one image upload is successfully one credit deducted this is looking great so let's do a quick recap on the media uploader component in here we're using two of the next cloud in a race very important components CLD upload image which is the entire feature full widget allowing you to upload images from different sources and then the CLD image that allows you to render the most optimized version of the image depending on exact widths and heights you want to provide to it and you can also do this nice place holder you saw how we're doing a shimmer effect while the image is loading so when you peer those two you get the uploader for the first time when you don't yet upload the image and then you get the image showcase once the image is there now what's happening is that on success we are modifying all of this state data which will be able to utilize later on within our form but for now it looks like the right side of the screen is a bit empty here right we have the original but the whole point of our app is that we can do crazy things with the images we upload such as removing different objects maybe like this cabin right here to make it full of nature or doing re coloring restoring the image filling out the image to get more of its surroundings so let's do that right away let's add the transformed image component on the right side so that we can finally apply different transformations to the original image to do that we can create a new component within our components shared cold transformed image dot sx we can run r a f c e within there and we can immediately use that image within our form so we can go to the transformation form and right below where we call this custom field still within the same div we can render the transformed image will pass a couple of props to it such as the image itself we can then pass the type to it as well let's pass a title of the image in this case we can get it from form dot get values and then we can call the dot title on it we can pass the state if it's currently being transformed so we can say if it's transforming we can set it's transforming as well so we know when to modify that state and we can pass the transformation config so we know exactly what we have done to it make sure to spell it properly transformation config now we can move over to that transformed image and accept all of these nice props that we just passed into it so that will be image type title transformation config is transforming and set is transforming like this later on we'll also have a special property of has download which we can do default to false and all of that can be of a type transformed image props which we have already defined within the index dts and we can start creating the layout of our transformed image I'm going to pull the code just a bit here and I do believe that we should be able to see the outcome of this transformed image so let's start with wrapping everything in a div that has a class name equal to flex flex dash call and gap of four within it we can create another div that will have a class name equal to flex between and right within that div we can create an h3 that will have a class name equal to h3 dash bold and text dash dark dash 600 there we can say transformed I hope this is starting to make more sense now we have the original and we have the transformation sometimes we might also have a download button so we can say if has download and end in that case we want to render a button like this that will have a class name equal to download dash btn and on click it will call a callback function with an event called download handler to which we can pass that event I think we can also shorten that and call it like this button class name on click and then download handler like so which of course we have to define right here at the top const download handler for now we can leave it empty within that button we can render an image that will come from next image it will have a source equal to forward slash assets forward slash icons forward slash download dot SVG it will have an alt tag of download a width of 24 a height of 24 and a class name equal to padding bottom of six pixels within square brackets we cannot see it now because it said to fold it and it says it will have a source equal to 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height of 24 a height and then return a div and properly close it that has a class name equal to relative and if we don't yet have the image then we can simply render a div which we can also close it will have a class name equal to transformed dash place holder and it can simply say transformed image if we save it there we go we simply have a div that says transformed image but now what happens once we actually upload our image if we go back to our unsplash and select an image you'll notice that it will get uploaded but nothing will yet happen to the right side although our CSS works well that it changes the size immediately the transformed image is not doing anything yet and that's because we haven't yet clicked apply transformation for that we'll need to do some extra logic calling cloud and areas APIs to do actual changes on the image and that can be as I said restoring the image removing some objects filling out the image so we get more of the environment and so on we'll do that soon enough but just to ensure that we immediately see something once the transformation is done let's implement the cloud memory image to show that transformation to get started we can use the same one from the media uploader which is this CLD image right here so let's go ahead and copy it and then paste it directly within this div of course we have to import it from next cloud memory and it would be nice if we just invented properly as well so I'm going to make a selection and invented right here there we go let's fix this thing that I messed up data URL as placeholder let's immediately import everything we need such as we get image size from lib utils data URL from utils and placeholder URL from utils as well now instead of passing the public ID as the source will pass something else we'll pass image question mark dot public ID because now that public ID is already stored within the image state the Alt tag will be image dot title the sizes can remain as they are same thing for the placeholder for the class name we can give it a transformed dash image class and now this is important we can give it the on load property where we can call a specific callback function as well as on error handler so what will happen if we experience an error while trying to load it on load we want to turn on the set is transforming state so here we can check if we have access to the set is transforming function and if that is the case then we can call set is transforming to false because the image has been loaded on error we can call a debounce function from lib utils we can call it as a callback function set it to something like eight seconds which is 8000 milliseconds and then we can call the same thing right here set is transforming to false because if nothing happens after eight seconds then we can surely say that it has failed and maybe the most important thing of this entire CLD image which we can do at the end right here is to spread the entire transformation config because this config will contain all of the image transformations that we want to apply to that image and finally right below that image while we are transforming we want to show some loaders so here we can render a div we can close that div as well and that div will have a class name equal to transforming dash loader and within it we can render a next GS image which of course has to be imported first from next image and then we can give it a source equal to forward slash assets forward slash icons forward slash spinner dot SVG we can give it a width of about 50 a height of about 50 and then I'll tag off transforming now if we click save absolutely nothing will happen that's because we are still under this placeholder of transformed image as we haven't yet applied the transformation so with that in mind what do you say that now we go back to our transformation form and fix this issue first it should be a lowercase s see how easy it is to notice these typos once you have typescript enabled and scroll all the way to the top where we want to actually deduct the credits from the user and we want to do something on submit which is to apply the transformation so let's get started with updating these credits first we can do that by navigating over to user dot actions dot TS where right now we have create get update and delete and the last user action will be to use credits and it will follow the same form such as export a sync function update credits that accepts a user ID of a type string as well as a credit fee which is equal to a number and we open up a try and catch block in the catch we call the handle error function and pass the error to it while in the try we immediately try to await connect to database once we connect we want to update the user credits by saying const updated user credits is equal to await user dot find one and update we want to find the user by the underscore ID property which is equal to user ID then we want to increment by using the ink command the credits and we want to apply it a credit fee which will most likely be something like minus one and finally we want to give it options of new to true so we can recreate that user if there's an error like if there is no updated user credits we say error credits update failed and then we simply return Jason parse Jason stringify updated user credits so we know exactly what has happened and that is our server action for updating the credits so we can now go back and we can uncomment this line right here and imported from update credits coming from user actions and for now we can hard code this credit fee to minus one I'm going to leave it to do right here to potentially update credit fee to something else if needed or to make it a bit more dynamic but for now it can be minus one great so now before we can actually submit the form and do the transformation we have to implement the server actions that will call cloud in a reason apis so to do that let's close our form go right here to our lib actions and create a new file called image dot actions dot t s and of course immediately add the use server directive at the top as this will be a file containing many different server actions to add images, update them, delete them and get image information by ID let's start with the first server action of the day it's going to be the one to add the image to our database so we can say export a sync function add image and it can accept a couple of apps such as image user ID of the user who's uploading the image as well as the path to which we need to navigate after the image is added and these will be a type add image perhaps immediately within it we can open up a new try and catch block you know the drill within the catch block we can create a new handle error where we can lock the error and then within the try we can simply await connect to database like this at the end of every single server action we also want to do a revalidate path which is coming from next cache which will allow us to actually show the new image I was created and not just keep what was cached rather we want to revalid the path to show this new added image and at the end we can simply return Jason parse Jason stringify and then we pass in the actual image now this is just a typical boilerplate structure for almost every single server action so what do you say that we duplicate this three times below one two three now the first one is to add the image the second one will be to update the image so instead of calling it add image we can call it update image and it's going to be accepting the same three props and we can also change it to update image perams immediately after we have the one to delete so right here we can say delete image and we can rename the function to delete image this one simply needs the image ID to delete it so we can remove this entire object and just say image ID of a type string there we go and finally the last one is to get it by ID so we can say get image and this one we get image by ID of course we need the image ID and that's the only thing we need so we can say image ID of a type string so now let's start from top to bottom and let's implement these different server actions in the first one we need to connect a specific image to an author who created it so right here we can say const author is equal to a weight user which is imported from database models user model dot find by ID and then we pass in the user ID of course ID is capitalized then we want to check if maybe we didn't get the author and if we did get it we can form a new image by saying const new image is equal to a weight image dot create inside of which we spread all of the image data and also pass in the author specifically the underscore ID of the author that created it this image should be coming from the models and it looks like we imported it from the wrong place so rather let's say image and let's imported from database models image model there we go and finally at the end we want to return a newly created image that we added to the database this was at image now for the update it will be very similar after we connected database we want to find the image to update by first running the dot find so we can say const image to update is equal to a weight image dot find by ID and into it we pass the image dot underscore ID after we find it we need to check if it actually exists and the weather our user has the permission to update it so we can say or image to update dot author dot to hex string so we're getting its ID is not triple equal to user ID in that case we can say unauthorized or image not found finally we want to update it by saying const updated image is equal to a weight image dot find by ID and update not delete we want to do the lead in the next one and we want to pass it three different perhaps first the image to update dot underscore ID so we know which one we're updating that's not within an object then the image object itself meaning the data that we want to update and then new to true to create a new instance of that document once we have this image to update we simply want to pass it over right here and return it and with that the update image is done as well delete will be fairly simple we need to connect to the database and we need to run a weight image dot find by ID and delete and we pass the image ID in this case we don't need to revalid anything because the image is gone and we also don't need to return it because there's nothing to return so this is pretty simple to this one though we want to add a finally clause so what's going to happen once the image has been deleted we simply want to use the redirect property coming from next navigation and just redirect to forward slash meaning the homepage that is it for our delete and now for our get we want to connect to the database and then get that image we're looking for by saying const image is it going to be a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a new instance of the image that we want to add to the database and then we want to add a spread out all of the transformation configuration.</p><p>
 So transformation, config.</p><p>
 I believe this should be good and there we go.</p><p>
 Then we get back the transformation URL provided to us directly by CloudNary.</p><p>
 Next, we want to form all of the image data by creating a new object and then giving it a title such as values that title coming directly from the form, a public ID coming from the image, a transformation type, which in this case can be just type because we're storing it.</p><p>
 Is it a recolor? Is it a generative fill or something else? Then we pass the width equal to image width, the height equal to image height, the config, which will be equal to transformation config, the secure URL to where that image is stored, which is equal to image that secure URL, like this.</p><p>
 Then the transformation URL, the aspect ratio equal to values that aspect ratio that's coming from the form, the prompt in case there is a prompt equal to values that prompt and the color in case we're doing a recolor.</p><p>
 This is all that we need for the image data.</p><p>
 Finally, we can see if the action is triple equal to add.</p><p>
 That means that we want to add the image for the first time.</p><p>
 In that case, we can open up a new try and catch block.</p><p>
 In the catch, we can simply consulog the error.</p><p>
 But if we are in the try, we can get this newly generated image by saying const new image is equal to a weight, add image, which we can call and then provide an object and give it exactly what it needs.</p><p>
 Now, this add image has to be imported from image actions.</p><p>
 And this is what we created not that long ago.</p><p>
 So we know that it accepts the image, the user ID and the path.</p><p>
 So moving back, first we need to turn this function into a sink right here.</p><p>
 So a sink function on submit.</p><p>
 And we can pass it the image equal to image data, the user ID and the path of just forward slash in this case, it looks like there's a type mismatch.</p><p>
 So let's see in our add image, add image programs, the image has the following fields title, public ID transformation type with hide config secure URL transformation URL aspect ratio prompt and color.</p><p>
 And let's see what we're passing to it title public ID transformation type with hide config secure.</p><p>
 Oh, yeah, I think the URL here was supposed to be all uppercase secure URL.</p><p>
 Same thing goes for the transformation URL right here equal to transformation URL.</p><p>
 There we go.</p><p>
 And now types with this no longer complaining.</p><p>
 It knows exactly what it needs to get.</p><p>
 Finally, if we successfully create a new image, what do we want to do? Well, we want to run the form that reset to do a bit of a cleanup.</p><p>
 We want to set image data or set image to be just the data.</p><p>
 And we want to enable the routing functionalities, which we can do right at the top by saying const router is equal to use router.</p><p>
 And this has to be imported from next navigation.</p><p>
 So now at the end, we can say router dot push and we want to push to a template string of forward slash transformations, forward slash and then dynamic code new image dot underscore ID.</p><p>
 So we're going to push to that specific URL.</p><p>
 Also, what's going to happen if the action is not add rather if it's update.</p><p>
 So to replicate that, we can copy this entire try and catch block and then go below this if and add another if if action is triple equal to update.</p><p>
 In that case, we want to paste that same block, but this time will say update image, pass it the user ID, pass it the image, but not equal to image data.</p><p>
 Rather, we want to spread what the image data, the image currently has.</p><p>
 And then also pass the ID of the data dot underscore ID.</p><p>
 And we want to pass it a path, which will be equal to forward slash transformations with the S at the end.</p><p>
 We want to make it a template string and then do forward slash dynamic block data dot underscore ID, because now we already know to which ID we want to go to and don't forget to import update image from image actions.</p><p>
 Once you do that, we're going to call it not new image rather updated image.</p><p>
 And in this case, we don't have to reset the form or set the image data.</p><p>
 We simply want to push over to that existing transformation.</p><p>
 And at the end of both of these ifs and just one space before we close this entire function, we want to set is submitting to false because hopefully we have successfully submitted our form.</p><p>
 And with that, our submit is now done.</p><p>
 So let's go ahead and collapse it right here.</p><p>
 So let's give it a spin back in our form.</p><p>
 We can try with generative fill first.</p><p>
 This is a very exciting one.</p><p>
 Let's first select an image by going here and going to unsplash.</p><p>
 Let's do some interesting scenery.</p><p>
 So if I go to scenery, for example, let's do these natural rock bridge and click next.</p><p>
 I'm going to choose a medium size and click upload.</p><p>
 I'll say a natural rock bridge and I'm going to select a size.</p><p>
 Let's say that I want to make it a phone portrait, which this is obviously not.</p><p>
 So let's apply a transformation.</p><p>
 We have this nice looking shimmer effect while it is loading.</p><p>
 And you can see that it actually expanded the image.</p><p>
 It gave it more depth and it looks like we took it from a different vantage point.</p><p>
 We have more sky and we have more rocks at the bottom.</p><p>
 Pretty extraordinary, right? So what would happen if we click save image? I'm going to click it and something quickly happened.</p><p>
 But let's inspect our console to see more details.</p><p>
 It looks like we have an invalid schema configuration.</p><p>
 URL is not a valid type at secure URL.</p><p>
 So we have to be careful with this URL uppercase or lowercase.</p><p>
 We make sure that what we have on the front end matches what our back end and our database are expecting.</p><p>
 So let's fix it.</p><p>
 We can first look into where we're submitting this function and how we're sending it over to the back end.</p><p>
 I can see that here we say secure URL.</p><p>
 And once we're saving it into the image, it is secure URL like this.</p><p>
 And these are the only two mentions of this property in this file.</p><p>
 So we're then passing this image data over to add image and over to update image as well.</p><p>
 So let's see what those functions are accepting.</p><p>
 The ad image, if we look into the ad image, per ramps is expecting a secure URL with the URL capitalized.</p><p>
 And I believe is the same thing right here secure URL for the update per ramps.</p><p>
 So if we look into this means that we're good because we're indeed passing it as the secure URL all capitalized.</p><p>
 But the next question is now that we create this image and we pass all these properties to image that create we have to see what the image model itself wants.</p><p>
 And in this case, if we check it out looks like we have it as secure URL where the R and L are not capitalized same thing for the interface right here.</p><p>
 So let's go ahead and fix it and make it secure URL like this.</p><p>
 There we go.</p><p>
 Now if we search across the entire code base for secure URL, you can see that all instances of secure URL are all uppercase besides this one, which is interesting where we have the on upload image handler.</p><p>
 Where we set it to the state.</p><p>
 So what we can do is we can also make it uppercase here secure URL.</p><p>
 And don't forget there is one final instance then considering that we change that one as well.</p><p>
 And that is the image that secure URL right here.</p><p>
 How do they miss that one? It's interesting why didn't show up here, but here it does show up image that secure URL.</p><p>
 Anyway, I'm going to modify that one to be secure URL as well.</p><p>
 So in absolutely all instances where we're mentioning secure URL, we want it to be secure URL like this.</p><p>
 Great.</p><p>
 Now with that in mind, let's try to recreate a transformation and save our image to the database going back to our app.</p><p>
 We can select an image.</p><p>
 We can choose some of the unsplash images.</p><p>
 In this case, we're doing generative fill.</p><p>
 So let's try to use this one.</p><p>
 Some new images appear today.</p><p>
 And let's select a medium size uploaded.</p><p>
 This appears to be some kind of a person in the desert.</p><p>
 So that's how we can call it.</p><p>
 And the aspect ratio we're going for can be a phone portrait.</p><p>
 Let's say we want to use this image for social media.</p><p>
 So we can apply a transformation.</p><p>
 You can see this nice loading animation.</p><p>
 And there we go.</p><p>
 You can see how it expanded it.</p><p>
 You even see some steps right here.</p><p>
 It's a looking phenomenal.</p><p>
 You can also see her legs now.</p><p>
 That is great.</p><p>
 So now let's open up our inspect element.</p><p>
 Go to the console and clear it.</p><p>
 And let's try to click save image to see what happens now.</p><p>
 I click it and we get another error saying that invalid schema configuration URL is not a valid type at pad secure URL.</p><p>
 Okay, okay.</p><p>
 That's my bad.</p><p>
 If we go back to image that model that T.</p><p>
S.</p><p>
 I noticed that it typed URL, but URL is not a valid type.</p><p>
 We should have just used a string.</p><p>
 Both here in the secure URL as well as the transformation URL.</p><p>
 My bad.</p><p>
 I thought that Mongoose also supports URL types, but looks like I was mistaken.</p><p>
 So we can just use a string.</p><p>
 If we fix that and click save image one more time.</p><p>
 This time it looks like we get navigated to the transformations page, which is a good sign.</p><p>
 Because that happens on success.</p><p>
 And if we revisit our MongoDB Atlas cloud database, you'll see that we have a new image created within our database.</p><p>
 It's a transformation type of a type fill with a person in the desert, what is specific with height and the configuration, which in this case is fill background secure URL of the image that we modified.</p><p>
 And it's also connected to the author that created it.</p><p>
 And we can just cross verify that it's ff4.</p><p>
 If I check out users, you can see that my user right here also ends in ff4, which means that they are really nicely connected with that in mind.</p><p>
 And before we go ahead and create this transformation page, let's just ensure that the user can indeed do an image transformation.</p><p>
 And to do that, we have to make sure that they have enough credits.</p><p>
 Don't forget soon enough, we're going to implement the entire credit system where the user will be able to purchase credits to then use to do transformations.</p><p>
 So let's go back over to generative fill and let's implement a model that will let the user know in case they have insufficient credits.</p><p>
 So back in our application, we can head over to components and create a new shared component called insufficient credits model dot TSEX.</p><p>
 And of course, we could just copy and paste the entire model coming from Shatsy and in this case, I will provide you with a complete insufficient credits model down in the description.</p><p>
 So you can simply copy and paste it right here.</p><p>
 As you can notice, it's a very simple model where you wrap everything into an alert belonging to Shatsy and precisely the alert dialogue, which is a dialogue that interrupts the user with important content and expects a response.</p><p>
 So here you can say, are you absolutely sure and then continue or cancel? We've done a slight modification to it where it will actually say, hey, you run out of the credits, no worries, you can grab some more.</p><p>
 And then you have the buttons to go back to the profile and cancel or to proceed.</p><p>
 Of course, to be able to use this, we have to install the alert dialogue coming from Shatsy and.</p><p>
 So to do that, we can clear terminal and run mpx shatsy and dash UI add latest add alert dash dialogue.</p><p>
 Great.</p><p>
 And as soon as it is installed, you can see that it's no longer complaining.</p><p>
 Now another thing we need to do is go back to the user dot actions dot TSES where we have this update credits function.</p><p>
 Remember what we're doing here, we essentially just update the credits by modifying the credit fee, depending on which action user decides to take.</p><p>
 And then we return the updated user credits.</p><p>
 So now we have to go back to the form, which is the transformation form.</p><p>
 And we have to add a condition where this new model will show.</p><p>
 So right below the form, which is right here within this regular form element, we can create a new property where we can check for credit balance.</p><p>
 So we can say credit balance.</p><p>
 And if it is lower than math that apps, this is going to get the absolute value.</p><p>
 So if it's like minus five, it will bring back five, then we pass the credit fee, which will usually be negative.</p><p>
 And this credit fee is coming from constants.</p><p>
 So basically currently we're sending it to minus one.</p><p>
 If that is the case, then we want to render the insufficient credits model.</p><p>
 And of course, we want to render it as a self closing component.</p><p>
 There we go.</p><p>
 Now to quickly check out how many credits we have, we can go back over to users.</p><p>
 And we can see that our credit balance is 10.</p><p>
 I don't believe we have deducted any credits so far.</p><p>
 And I can see some other people also creating their accounts on the website, which is pretty cool.</p><p>
 But yeah, right now it seems that my amount of credits is still 10.</p><p>
 So let's see if it actually gets deducted right now to test it.</p><p>
 We can see what happens on submit specifically on transform handler.</p><p>
 And we even have it to do for ourselves here update credit fee to something else.</p><p>
 So right here, we are updating the credits to be credit fee.</p><p>
 There we go, which is coming from constants.</p><p>
 And it's basically the same minus one number.</p><p>
 And immediately as soon as we started the transformation, one credit should be deducted from our list of credits.</p><p>
 Because we are incrementing the minus one number.</p><p>
 But in this case, it shouldn't really be credits.</p><p>
 It should be credit balance.</p><p>
 Yes, that's why it wasn't deducted for the first time.</p><p>
 We have to be careful about how we call it.</p><p>
 So I'm going to simply say credit balance.</p><p>
 There we go.</p><p>
 And now we're properly updating the credits.</p><p>
 Now so far, we have only tested generative fill, but there are many other features to test.</p><p>
 Generative fill works where you just click the button and it does the job.</p><p>
 Then it takes the credits.</p><p>
 But for the background remove and image restore features, we need to set the new transformation and enable the apply transformation button right after the images uploaded.</p><p>
 It's going to make more sense soon.</p><p>
 To do this, we can use the use effect to watch for changes.</p><p>
 So let's go to image restore.</p><p>
 Go back to our code, specifically the transformation form.</p><p>
 And navigate over just at the top of our form where we can add a new use effect.</p><p>
 This use effect, of course, has to be imported from react.</p><p>
 And it accepts a callback function as well as a dependency array.</p><p>
 In this case, in the dependency array, we want to look for when the image changes, when the transformation type dot config changes and when the type itself changes.</p><p>
 Then we want to say if the image exists and in parentheses, type is triple equal to restore or type is triple equal to remove background.</p><p>
 In that case, we want to immediately apply the set new transformation equal to transformation type dot config like this.</p><p>
 So now to check out how this works, let's try to do either a restore or remove background transformation together.</p><p>
 I'll do a restore first.</p><p>
 Let's try to search for an image.</p><p>
 I would want to find a very bad quality image.</p><p>
 So very pixelated.</p><p>
 Let's try to see pixelated image.</p><p>
 Yeah, all of these are pretty good, not pixelated in any sense.</p><p>
 Let me see bad quality image.</p><p>
 Maybe this court image right here seems a bit of a lower quality.</p><p>
 Let's see.</p><p>
 Yeah, we can go for the small version and click upload.</p><p>
 You can see that the one credit was immediately taken.</p><p>
 We can give it a title such as let's do court image restore or let's just say court and click apply transformation.</p><p>
 Oh my god, you can immediately see the quality difference right here.</p><p>
 The image on the right is much, much sharper.</p><p>
 I hope you can see it.</p><p>
 If not, I'm going to zoom it in just a bit more.</p><p>
 And it definitely has a lot more detail.</p><p>
 So the image restore feature works wonderfully well as well.</p><p>
 I can even open both side by side and just look at the difference in quality.</p><p>
 Let's say here and here.</p><p>
 The left image has much more detail and depth and color and contrast as well.</p><p>
 And if you're paying attention as soon as we uploaded the image, we should have spent one credit.</p><p>
 If we go back to our database and refresh, we can see that now we have nine credits remaining.</p><p>
 That should be enough for us to test the application to the point until we implement stripe to purchase new credits.</p><p>
 Now let's also test out the background remove, which is a very cool feature.</p><p>
 So let's go here and let's try to find something where we might want to remove the background from such as some kind of a portrait of a person.</p><p>
 This one is cool, but there's a lot of color coming in.</p><p>
 These are interesting portraits.</p><p>
 But let's try to find just a typical LinkedIn type portrait right here.</p><p>
 Like this guy right here, that's pretty interesting.</p><p>
 And let's click next.</p><p>
 Select a format.</p><p>
 Let's go with large and click upload.</p><p>
 There we go.</p><p>
 We're loading up the original.</p><p>
 We can save portrait.</p><p>
 And we can run apply transformation.</p><p>
 But in this case, it still says transforming right here.</p><p>
 Because the background removed does take some time.</p><p>
 And there we go.</p><p>
 You can see that now we're getting the transformed version.</p><p>
 And it did a phenomenal job.</p><p>
 You can see there's not a lot of lost content right here around the edges.</p><p>
 It's not looking bad at all.</p><p>
 And we can click save immediately after we let the transformation page, which will develop later.</p><p>
 And if it's still not clear why for the restore and the remove we had to add that use effect.</p><p>
 That's because we have only a single field.</p><p>
 This one right here.</p><p>
 And these will not allow us to apply transformation.</p><p>
 So once the image is uploaded, we can immediately allow this button to be clicked.</p><p>
 Whereas with other ones, there's also some additional fields that will allow us to then apply the transformation.</p><p>
 And yes, I know that so far we have tested the restore, the fill and the background remove.</p><p>
 We're going to play with the object remove later on.</p><p>
 We cannot do that for now because we cannot type into this input and same things goes for the recolour will work on that very soon.</p><p>
 But for now, let's focus on implementing the homepage, the primary page of our application, where we can see all of the transformations that you and other people have made pretty exciting stuff.</p><p>
 So let's get started.</p><p>
 Our homepage won't be just the collection of cards will show to the user or all of the transformations we have created so far.</p><p>
 It will be a call to action to start doing these actions.</p><p>
 It will show you the transformations themselves.</p><p>
 And we will also use cloud in a research API to allow for incredibly detailed searches.</p><p>
 So to develop it, we can collapse this over to the side and navigate over to root and then page within the root, which is our homepage.</p><p>
 So first we want to create our homepage banner.</p><p>
 So right here at the top, we can wrap everything in an empty react fragment and create a section that will form our banner.</p><p>
 We can give it a class name equal to home and right within it, we can give it an H1 that will say something like unleash your creative vision with imagine if I.</p><p>
 And if we save it nothing appears yet that's because we can give it a class name equal to home dash heading, which should apply a bit of margin.</p><p>
 And then if you reload, we still cannot see anything.</p><p>
 That's because we don't want this to clutter the mobile view.</p><p>
 Rather, we only want this to appear on desktop devices, but already we can collapse it once again because now we can start focusing on the main content, which will be a UL an unordered list with a class name equal to flex dash center, W dash full and a gap of 20 right within this UL, we can map over all of our nav links by saying nav links that map.</p><p>
 But before we map over them, we first want to slice them.</p><p>
 So we're going to use a slice from 1 to 5.</p><p>
 And then we can map over each one of these individual links and immediately for each link, return a link component, which we can import from next link, each link will have a key equal to link that route.</p><p>
 It will also have an href equal to link that route.</p><p>
 And it will also have a class name equal to flex dash center flex dash call and a gap of two.</p><p>
 Finally, what do we show within the link that will be an ally and below the ally, we can create a p tag that will render the link that label.</p><p>
 So now if we save it, we again cannot see anything on mobile, but if we go to tablet or desktop, you can immediately see that we can unleash our creativity and also click on one of these transformations to get started.</p><p>
 This is a common practice that you'll see that on mobile devices, you want to remove some content because you don't have enough space to show it properly.</p><p>
 So it's better to focus just on the content, but in this case, if we do have the space, why not just put it in there.</p><p>
 So let's create an image within this ally, which will be a next GS next image tag that can have a source equal to link that icon.</p><p>
 It will also have an Altag of image, a width of 24 and a height of 24.</p><p>
 But now let's also style this ally by giving it a class name of flex dash center, W dash fit, rounded dash full to turn it into a circle, VG white and a padding of four.</p><p>
 That's much better.</p><p>
 Let's also style the p tag by giving it a class name equal to p dash 14 dash medium, text dash center, text dash white.</p><p>
 And if we save it looks good, but I just have to add a T to the text and this is now much better.</p><p>
 And now that we have this banner, which already navigates you to different pages, we want to focus on displaying some of the finished transformations that our users have utilized.</p><p>
 And to display all of these properties, we'll create a new component called collection within shared components by creating a new collection dot TSX.</p><p>
 And just to save you a bit of time and so we can focus on what matters the most, which is cloud and a research and also purchasing credits later on using Stripe and making transactions, you can find the complete collection dot TSX file in the read me down below.</p><p>
 So you can simply copy it and paste it here.</p><p>
 Here we don't do anything we haven't done yet so far.</p><p>
 We simply have a link where render the CLD image and then also display a title for that specific image, as well as render the image belonging of the type of the transformation we made to it.</p><p>
 That is it.</p><p>
 And let's not forget a part of this specific collection also has to be the search component.</p><p>
 So let's create another component right here under shared called search dot TSX.</p><p>
 And you'll also be able to find a complete search in the read me down below.</p><p>
 This component couldn't be any simpler.</p><p>
 We're simply using one single input where we modify the query for the search.</p><p>
 And what we're doing here is we're using the debounce function so that we don't automatically search through the API for every keystroke.</p><p>
 Rather, we wait for 300 milliseconds before we send the response.</p><p>
 And what we're doing here is we're not just updating the state.</p><p>
 Rather will also be updating the URL.</p><p>
 So as you type, for example, test right here and press enter, you'll notice that that test gets added as the query to our URL query.</p><p>
 So you can also paste the search right here.</p><p>
 And just so I don't forget our collection component will also be using pagination.</p><p>
 So right here, we're just importing the basic pagination coming from Shats Yen.</p><p>
 And that means that we have to install the pagination coming from Shats Yen UI.</p><p>
 So let's do that right away by clearing it out and saying MPX Shats Yen dash UI add latest add pagination and press enter.</p><p>
 There we go.</p><p>
 It automatically got installed.</p><p>
 And we don't have an error anymore.</p><p>
 You can also notice that we're using the search component right here within the collection.</p><p>
 There we go.</p><p>
 So now be the perfect time to check out how our collection component looks like on the homepage.</p><p>
 And we can do that by going over to root and then page.</p><p>
 Going below our links.</p><p>
 And below our section.</p><p>
 And we can create a new section that will have a class name equal to on small devices margin top of 12.</p><p>
 Within it, we can render the self closing collection component, which of course we need to import from components shared collection.</p><p>
 And we also need to pass a couple of props to it specifically props relating to the pagination.</p><p>
 So let me show you how we'll implement pagination in our app next.</p><p>
 She is makes it so easy.</p><p>
 You want to grab the page number directly from the search query per rams.</p><p>
 And you can do that by destructuring the search per rams directly from the props and giving it a type of search per rams props.</p><p>
 Once you have that, you can say const page is equal to turn it into a number and say search per rams question mark dot page or set it equal to one by default.</p><p>
 We can do the same thing for the search query by saying const search query is equal to you wrap it in a string and say search per rams question mark dot query as string.</p><p>
 Or we can leave it as an empty string by default.</p><p>
 Let's expand it so it fits in one line.</p><p>
 And the next thing we have to do is of course fetch all the images we have created so far so that we can then pass them into our collection.</p><p>
 To do that, we'll have to navigate over to our image actions dot ts.</p><p>
 Remember, we have actions to add the image update delete and get a single image.</p><p>
 But we don't yet have one to get all images.</p><p>
 So we can create one.</p><p>
 This will be the most complicated of them all.</p><p>
 And this is exactly why I'm sometimes providing specific pieces of the UI because that allows us to focus on what truly matters, which is more complicated pieces of code that you don't get to see in every YouTube video.</p><p>
 You get to see them in JSM videos because I want to make sure that we try to replicate the real work environment as much as possible to replicate how real life projects look like with all of the little intricacies and details.</p><p>
 So let's create it together.</p><p>
 First, we can follow the typical structure by copying what we have here from the get image and pasting it here.</p><p>
 We can rename it to get all images and it can accept a couple of programs that we can destructure.</p><p>
 That's going to be a limit which by default can be set to nine, a page which by default can be set to one, and a search query which by default can be set to an empty string.</p><p>
 And all of that will be of a type limit is optional of a type number page optional of a type number to and search query of a type string.</p><p>
 And I believe we don't have to make page optional.</p><p>
 We can always pass it at least a number one.</p><p>
 Then we're connecting to the database and we're handling the error.</p><p>
 But now we can focus on the most important part which is to get all images, but not only that also to search through those images to make the search work, but not only that also to make the pagination work all together.</p><p>
 So to make that happen, we'll have to configure our cloud and our instance to be able to pull the images from somewhere.</p><p>
 And for that reason, we have to install a new package called cloud anery, MPM install cloud anery.</p><p>
 Remember so far we have just installed next cloud anery.</p><p>
 And now that we have installed it, we can just import it right here at the top by saying import V2 as cloud anery from cloud anery and we can scroll down and then say cloud anery dot config.</p><p>
 And now we can pass a couple of our environment keys into it, such as the cloud underscore name, which we have stored in process dot ENV dot next underscore public underscore cloud anery underscore cloud underscore name, the API key, which we have also passed as cloud anery API key and API secret, which is cloud anery API secret.</p><p>
 Also, we can say secure is true to make sure that we have a secure connection.</p><p>
 Now you might have noticed that as I was typing this new lines immediately appeared for me and all of that is done by GitHub co pilot.</p><p>
 I'm not paid to say this, but it truly speeds up my workflow.</p><p>
 So I would recommend checking it out.</p><p>
 I do believe they have a free version for students, maybe even free one month versions you can try it out, but it's definitely a great help.</p><p>
 Now, now that we have a cloud anery config, we can also set up our initial expression by saying let expression is equal to and here we can say folder is equal to and you need to make this equal to the name of your folder.</p><p>
 I believe I use just imagine if I, but if you go back right here to your dashboard, increase the size a bit and go to right here to your specific upload preset, you can see the folder name right here and just copy and paste it next.</p><p>
 Next, the reason why we just said this expression like this is because now if there is a search query, then we can append the search to it by saying if search query exists.</p><p>
 In that case, we can say expression plus equal to a template string of space and and then we can put the search query right here, which will search only for specific images.</p><p>
 Finally, we can get the resources back by destructuring them from the call will just make and that is equal to a wait, cloud anery dot search and then we can pass the dot expression, pass the expression in and then say dot execute.</p><p>
 This will give us back all the resources we need now that we have gotten back those resources, we need to get back the resource IDs so we can also get them from our database and we can do that by saying const resource IDs is equal to resources dot map where we get each individual resource and for each resource we return the public underscore ID.</p><p>
 We can also add a type any for this right here now that we have our resource IDs, we can form a new query which will use for querying our own database and we can say if search query exists that's coming from the front end, then we want to modify the query to go over the public IDs and say in included resource IDs.</p><p>
 So we're now modifying our query search to include only the ones that we got back from cloud anery then to implement the pagination we can define a skip amount, which is just a number of cards we want to skip because we're on the second page for example and we can define this by wrapping the page into a number like this and then saying minus one and then multiply it by the limit the limit of course being the limit of cards per page.</p><p>
 So this will give us the specific skip amount and finally we can now fetch back the images by saying const images is equal to a weight populate user because we also want to get back the user data for each image so we know who created it and then we want to run the image that find to which we pass the query like this.</p><p>
 We can also run the dots sort on it to sorted by updated add minus one so the newer ones appear on top we can also run the dot skip skip amount for the pagination and that limit of limit.</p><p>
 Finally we need to define the number of total images so we know how many there are by saying const total images is equal to a weight image dot find where we pass the query and we call the count documents like this and we also want to get the total number of all images in general by saying const saved images is equal to a weight image dot find and we call it with the data.</p><p>
 We can now get the data without a query and then we call the count documents.</p><p>
 Once we have this we can return an object where the data is equal to Jason that parse Jason that stringify images where we have the total pages which is equal to math that seal so this tries to return the smallest number that's greater than or equal to the argument that you pass in and that's the total images divided by the limit so we know how many total pages there are and then we pass all the data.</p><p>
 So we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now Now we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data and we can now get the data So here we're just using some images and components we have already used before and for that reason I'm going to provide you with a completed transformations ID file page.</p><p>
 You can copy from the readme and paste it right here.</p><p>
 You can quickly see that this is nothing more than a simple section where we show the transformation type, the prompt if there is one, the color if there is one, the aspect ratio if there is one, and then we show the image before and image after that is it.</p><p>
 We also need to import the header from shared components header.</p><p>
 Here we're doing the default export and we just have to make it default import as well and we're good.</p><p>
 And now if we go to the browser we're going to an error saying that we need to convert some kind of a component into a client component specifically referring to some kind of a button here.</p><p>
 This is a great lesson to look into.</p><p>
 This is an async component so it surely has to be server component or server page.</p><p>
 But some of the components here apparently some kind of a button should be client side.</p><p>
 And here we have a regular button.</p><p>
 This is not a button with some kind of a function that we have seen.</p><p>
 So this is not it.</p><p>
 But if we navigate into the transformed image component, you can notice that this component has a button.</p><p>
 That has an on click listener and whenever you have on click listeners and click handlers, that means that this component has to be a client component.</p><p>
 And the reason why we have seen this error just now is because on our image details page, we have the download button.</p><p>
 So it actually renders for the first time ever will implement this soon.</p><p>
 With that in mind, if we go back this time, we get an error saying invalid source prop hostname rescloudnary.</p><p>
com is not configured under your next config.</p><p>
 So yes, so let's copy this rescloudnary.</p><p>
com.</p><p>
 This is the host hosting our images go to next.</p><p>
config.</p><p>
mjs and right within say images.</p><p>
 Then define the remote patterns, then an array with an object in between with a protocol of HTTPS and make sure to spell it correctly.</p><p>
 Then give it a host name like this, rescloudcloudnary.</p><p>
com and we can leave the port as empty.</p><p>
 Now if we save this and the recompiler application, we can see our product details.</p><p>
 It is as simple as it gets.</p><p>
 We just say some stuff right here and then we show the original and the transformed image.</p><p>
 Let's just make sure that this download button works.</p><p>
 To fix it, we know where we have to go.</p><p>
 In the transformed image and we have to implement this download handler.</p><p>
 Right here, we can accept the event as the first and only parameter and say that it's of a type react dot mouse event of a specific HTML button element type.</p><p>
 And it has a mouse event as well like this.</p><p>
 Immediately, we want to run the e.</p><p>
prevent default to prevent the default behavior of the browser which is to do a reload and then we can say download.</p><p>
 This is coming from lib utils.</p><p>
 To it, we need to pass which image we want to download, specifically the URL of the image and we can do that by saying get CLD, image URL coming from next cloudnary, to which we pass an object where we define the width of the image.</p><p>
 In this case, image question mark dot width, the height of the image, image question mark dot height and the source of the image, which is public ID and then we pass over all of the transformation config just like this.</p><p>
 Finally, we have to pass the second parameter to the download function, which is the title.</p><p>
 As you can see, now it's happy and we can look into the download.</p><p>
 Basically, the way it works is it does a fetch to a URL from the response.</p><p>
 It gets access to something known as a blob, which basically contains the image forms it as a URL and then downloads it.</p><p>
 I believe this is a function that I created using Chad GBT.</p><p>
 Sometimes you don't want to write these functions by hand.</p><p>
 And now if we go back and click right here, this great looking image just got downloaded.</p><p>
 Great.</p><p>
 So with that in mind, our image details page is now done.</p><p>
 You can see the before and after.</p><p>
 So the next thing we'll do is we'll implement the image update page where we can update a specific transformation.</p><p>
 This will be incredibly easy as we have already built the original form.</p><p>
 The update form will in most cases be almost exactly the same as the create form, but it will have the information prefilled, such as the image title, original, transformed and so on.</p><p>
 So you can immediately see what did you do on that image before.</p><p>
 So we can create that update form by going to pages, route, transformations, ID and then update page.</p><p>
 Since we have already built this form together before, you can guess it.</p><p>
 I'll provide this code to you in the readme down below.</p><p>
 So copy it and paste it right here.</p><p>
 Believe it or not, it has only 40 lines because it's using the header component, which we have already created as well as the entire transformation form, which is that biggest component that you have created completely on your own while watching this video.</p><p>
 Yep.</p><p>
 So we're simply reusing it here.</p><p>
 In this case, we have to fix a couple of imports, such as this header needs to be a default import.</p><p>
 And then we need to see if we have access to the get user by ID and get image by ID actions coming from our actions.</p><p>
 Looks like we cannot find this module right now.</p><p>
 So what if we go into image dot actions and search for it will be do have get image by ID.</p><p>
 And if we go into the user dot actions, we have the get user by D.</p><p>
 So it looks like just the imports are wrong.</p><p>
 Let's delete them.</p><p>
 And we're recalling them.</p><p>
 Let's just automatically import them by pressing control space and clicking right here.</p><p>
 And looks like the imports are now good, which is a bit weird because I think they were exactly the same before we deleted them.</p><p>
 But here it looks like they're good.</p><p>
 And also the subtitle will be renamed to a lowercase T.</p><p>
 But if we do this, go to the image details and then navigate over and then append the forward slash update to the URL.</p><p>
 You see that you'll be navigated to this form like page where you can now modify it.</p><p>
 Where you can now modify this transformation.</p><p>
 For example, let's say I want to make this square, click apply, but it looks like we're stuck on transforming.</p><p>
 So let's see what's wrong.</p><p>
 Back in our code, we can go back to the transformation form right here.</p><p>
 And specifically go to where we're working with the fill property right here.</p><p>
 So if type is fill, let's see what's happening.</p><p>
 Well, in this case, we have this select field that has the on value change.</p><p>
 But it also needs to have the actual value so we know once the value has changed.</p><p>
 So we can pass it field dot value.</p><p>
 Then also going to the prompt field right here.</p><p>
 We are destructuring the field, but it looks like I have an extra pair of parentheses right here.</p><p>
 So I can just remove that one extra pair and close it properly.</p><p>
 It should look something like this.</p><p>
 Also, quickly navigating to the transformation image or transformed image right here, where we have the debounce.</p><p>
 This function that we declare right here won't be called on its own.</p><p>
 So to invoke it, we have to make it into a self invoked function expression, which will look something like this.</p><p>
 I noticed that I missed this before.</p><p>
 Also, we need to fix this loading for the transformation image, where I notice we're calling it a transforming loader.</p><p>
 But if we search for classes, there's not going to be one.</p><p>
 Rather, I misspelled it in the globals as transforming.</p><p>
 So rather, it should be a transforming loader.</p><p>
 So add an s right here.</p><p>
 And we can also call this an alt tag of spinner.</p><p>
 And below it, we can give a p tag of something like please wait so that we also know that something is happening with a class name equal to.</p><p>
 Text-white over 80 to make it a bit grayish.</p><p>
 And the actual transformation might have worked for you even before.</p><p>
 But I just wanted to make sure that we do a couple of these quality of life changes as I like to call them.</p><p>
 So with that in mind, I created another one, but we can go back to the person in the desert.</p><p>
 We can navigate over to update and make sure that you have enough credit balance.</p><p>
 If not, just manually increase it right here.</p><p>
 Otherwise, you would be able to see a model to purchase more, which we'll implement later.</p><p>
 But with that in mind, if we now try to change it to a square and click apply transformation, the please wait now is much better.</p><p>
 So we can see some visual cues that something is happening as well as it says transforming right here.</p><p>
 For you, it might have worked before as well.</p><p>
 But for now, it's visually being shown.</p><p>
 And after doing a bit of browsing, I managed to find a situation where I said aspect ratio on instead of ratio.</p><p>
 So if I now remove this end at the end, this was in the on select field handler.</p><p>
 Hopefully that will be one last obstacle we have to go over before we can fully test out our updating functionalities.</p><p>
 So now if I go back and if I go to the person in the desert and I go to update, we get navigated to the update form and I'll try to turn it into a square.</p><p>
 And as you can see, it even saved it in the cache because I was testing this out before.</p><p>
 So it works automatically.</p><p>
 But I can click apply transformation one more time to regenerate it.</p><p>
 Now sometimes, and depending on some regions, this generation can take a bit longer.</p><p>
 But we can always wait for it because now we have this nice looking loader.</p><p>
 And after some time, we got back the square.</p><p>
 Now, one issue that we have in our app right now is that if you check, we have to manually type update in the URL bar to go to the update page.</p><p>
 But a regular user might not even know that this update page exists.</p><p>
 So to fix that, what we want to do is immediately once we visit the details page, if we are the creator of this post, we want to create a button to update or delete that post.</p><p>
 And to get to that button, we can go to the transformations ID page.</p><p>
 That is this one right here, not update, but just the details.</p><p>
 And then if you scroll to the bottom of that page, you should notice that we already have this part that should allow us to update the page by navigating to the update page.</p><p>
 But it looks like we're not getting back the user ID.</p><p>
 At least not in the way we're currently trying to call it.</p><p>
 So instead of getting the user ID from the session claims, which would be getting it from the clerk metadata, we can try to get it directly from Auth by saying user ID.</p><p>
 And then we get it immediately.</p><p>
 So here, we're getting the user ID back from clerk, but then our image only knows the user by its database ID.</p><p>
 So we have to go into the get image by ID.</p><p>
 And then where we populate the user right here at the top, we also have to add the clerk ID.</p><p>
 That way, if we go back, this image will now have access to the clerk ID.</p><p>
 So if we go down, and now if we change this over to image.</p><p>
author.</p><p>
clrcid, now we have access to it, and our check should be proper.</p><p>
 So now we should be able to see the update image.</p><p>
 And if we go back, there it is.</p><p>
 And now it navigates us over to the update page, immediately see how this caching works really nicely, in case you want to really quickly modify it.</p><p>
 And you can save it immediately.</p><p>
 That is great.</p><p>
 The update button works.</p><p>
 With that in mind, let's also implement the delete button if we are the user.</p><p>
 Of course, created the post.</p><p>
 So to implement it, we can implement a model similar to the one we have had so far, which will be within components shared, and we can call it delete confirmation.</p><p>
tsl.</p><p>
 As this is just an alert dialogue, similar to the one we've used before, the full code will be provided in the readme down below.</p><p>
 So copy it and paste it here.</p><p>
 About 60 lines where we simply use the default example from Shats Yen, where we say delete image.</p><p>
 Are you sure you want to delete it? Yes.</p><p>
 And then we call the server action, which we have created before.</p><p>
 Great.</p><p>
 Now, of course, we want to call this delete confirmation component somewhere, and specifically, we want to call it on the same page where we just show that update.</p><p>
 And that is on the transformations ID page.</p><p>
 This one right here, right below the update, specifically below the button, we can render a self-closing delete confirmation component, and we need to pass to it the image ID, equal to image.</p><p>
unterscore ID, so we know which image to delete.</p><p>
 Of course, this is only also being shown if we are the creator or the author of that.</p><p>
 Transformation.</p><p>
 So now you can see the delete image, and if we click it, you can see this great looking, but also very simplistic model that allows you to delete that transformation.</p><p>
 Let's give it a shot.</p><p>
 I click delete, and we're re-navigated back to the home, and we can see that some users also added some new posts, so all of this is looking great.</p><p>
 But don't forget, so far, we have tested only three out of five functionalities.</p><p>
 The background remove, the generative fill, and image restore.</p><p>
 But finally, let's make our fields within the object remove, and object recolor work, so we can also test out those amazing functionalities.</p><p>
 Fixing it will be fairly straightforward.</p><p>
 We of course have to navigate back to our transformation form, and then we have to navigate to the on, input, change handler, where we're calling this transformation.</p><p>
 In this case, I noticed that I closed this debounce a bit too soon.</p><p>
 Rather, the return of the on-change field should have happened after the debounce.</p><p>
 So we do it like this, and I believe this is the only change we needed to make to make our fields work, because now we are returning the on-change field with a new value.</p><p>
 So now if we go back, and if we try typing something, it works wonderfully.</p><p>
 So let's give it a shot.</p><p>
 Let's try to remove an object, and recolor an object.</p><p>
 Let's go to our great cloud binary image upload, go to unsplash, and try to find something that we can remove.</p><p>
 Hmm, this one will be interesting.</p><p>
 Three surfers walking in, let's see, in what, into the ocean at sunset.</p><p>
 Okay, that's an interesting one.</p><p>
 What would you say if we just made this, you know, ocean at a sunset without these three surfers? Let's see if we can make that happen.</p><p>
 I'm gonna upload this image, and say this is ocean at the sunset, hopefully without the surfers.</p><p>
 And what do we want to remove from this? Let's say people.</p><p>
 And unfortunately, the apply transformation button hasn't yet turned on.</p><p>
 The reason why we cannot see it is because on input change handler, this debounce is never actually getting cold in the way that it's structured.</p><p>
 What we need to do is we need to make it a self-invoking function, like this.</p><p>
 At the end, you can just put a pair of parentheses, and it will self-invoke and then set this new transformation.</p><p>
 So that now, if we try it again, let's say sunset at the ocean, object to remove will be people, and then we can actually take the same one, and quickly upload it.</p><p>
 You can see now that we can actually apply the transformation.</p><p>
 So let's give it a shot.</p><p>
 We can see this great loading.</p><p>
 Please wait.</p><p>
 And it looks like it removed one person.</p><p>
 So you know what? I'm actually happy with that.</p><p>
 It looks like that person was never actually there.</p><p>
 So I'm gonna say two people watching the sunset at the ocean.</p><p>
 That's actually good.</p><p>
 And now we can save the image.</p><p>
 Great.</p><p>
 We can see the original.</p><p>
 We can see the transform.</p><p>
 We can download that image as well, updated and deleted.</p><p>
 And we can see exactly what transformation happened.</p><p>
 And it also showed on our homepage.</p><p>
 This is great.</p><p>
 Now, finally, let's test out the object recolour by choosing an image that has an interesting color inside it.</p><p>
 Let's search for bright colors or bright color.</p><p>
 And let's try to go with these flowers right here.</p><p>
 Purple orchids.</p><p>
 I think that might be interesting to recolour.</p><p>
 We can say flowers.</p><p>
 Object to recolour would be a flower, right? And I'm gonna choose a color of red.</p><p>
 And click apply transformation.</p><p>
 And there we go.</p><p>
 We had very, very bright red colors.</p><p>
 And we can click save image.</p><p>
 Wonderful.</p><p>
 So all of the AI transformation functionalities are now fully functional.</p><p>
 Let's not also forget that we have a search.</p><p>
 So if we search for flower, we can see it.</p><p>
 If we search for sky, we can see some sky in the picture as well.</p><p>
 Ice cream, maybe.</p><p>
 There we go.</p><p>
 That works as well.</p><p>
 So we have this phenomenal cloud and a empowered search based off of the context that it recognizes in the image.</p><p>
 We have background removed generative fill and more.</p><p>
 Let me know down in the comments which one of these functionalities do you like the most? I find the generative fill the best because you can truly change the image and it expands it and provides you with more context.</p><p>
 And now with that in mind, would you look at this? It feels like our app is completely done, right? All of the functionalities are working.</p><p>
 But to turn it into a complete SAS, we want to implement payments.</p><p>
 Yep, once you run out of credits, we want you to be able to purchase credits to be able to do more transformations.</p><p>
 In the deployed application, that will look something like this.</p><p>
 We have a header and a subtitle that we reuse.</p><p>
 But then we have a single card which is also a reusable component which we reuse three times and we create three different tiers of pricing.</p><p>
 And of course, all of this will be connected to your clerk account and through Stripe will process those payments.</p><p>
 And then finally, we'll create the profile page where we'll be able to show the credits available, the image manipulations you have done so far, as well as all of your recent edits.</p><p>
 Exciting stuff ahead, but now we'll move away a bit from our app, the UI UX and the front end, and we'll focus on completely setting up our entire Stripe infrastructure and our server actions for processing the payments.</p><p>
 So let's get started with that next.</p><p>
 First things first, we need to create an account.</p><p>
 So click start now, go through the entire signup process and then turn on the test mode.</p><p>
 Then head over to developers and go to API keys.</p><p>
 You'll need your publishable key, so let's copy it.</p><p>
 And going back to our codebase, we can go to dottynv.</p><p>
local and we can add a new subchapter of Stripe.</p><p>
 And we can call it next underscore public, underscore Stripe, underscore publishable underscore key.</p><p>
 It's always better to be descriptive than to shorten it.</p><p>
 Great.</p><p>
 Going back to the Stripe dashboard, the second thing we need after the publishable key is the secret key, which we might need later, so simply copy it.</p><p>
 And back in our ENV, we can call it Stripe underscore secret, underscore key, and we can add it here.</p><p>
 Finally, we can install Stripe by going to our terminal, expanding it, and running mpm install.</p><p>
 Stripe and add Stripe forward slash Stripe-js.</p><p>
 Now that we have installed it, we want to create our first transaction server action.</p><p>
 So, let's head over to our lib, actions, and create a new file called transaction.</p><p>
action.</p><p>
ts.</p><p>
 And inside of here, we'll create a connection between a Stripe transaction to our database, meaning we'll purchase the credits.</p><p>
 So, you know the drill, we have to define it as the use server, and we can create two new actions.</p><p>
 Export async function, check out credits.</p><p>
 This is to buy the credits, where we'll get the transaction right here, off a type, check out transaction for rams, and that's it.</p><p>
 Here, we can do everything related to Stripe.</p><p>
 First, we need to set up a Stripe instance by saying const Stripe is equal to new Stripe like this.</p><p>
 And we have to import the Stripe at the top by saying import Stripe coming from Stripe.</p><p>
 And then we can call it as a function or as a constructor.</p><p>
 And to it, we need to provide our Stripe secret key by saying process.</p><p>
nv.</p><p>
stripe underscore secret underscore key.</p><p>
 And we can add an exclamation mark at the end because we know that it actually exists, so type script has nothing to worry about.</p><p>
 Then we want to get the amount that we're trying to charge by saying const amount is equal to we turn it into a number from the transaction that amount, and then we multiply it by 100 because Stripe processes the transactions in sense.</p><p>
 Once we do that, we want to create a new Stripe checkout session by saying const session is equal to a wait Stripe.</p><p>
checkout.</p><p>
 .</p><p>
sessions.</p><p>
create and want to pass in.</p><p>
 Perhaps first, we want to define the line items we're trying to purchase, which is an array that has one object.</p><p>
 And here, we can define the price underscore data, which is an object where we can define the currency.</p><p>
 In this case, we can go with USD.</p><p>
 The unit amount will be the amount we have declared above.</p><p>
 And the product underscore data will be a name of transaction dot plan.</p><p>
 So we'll have many different plans to choose from.</p><p>
 And then after this price data, we can define the quantity right here after quantity of one.</p><p>
 Then after this array, we want to define the metadata of this order where we can set the plan to be equal to transaction dot plan.</p><p>
 We can define the credits, which will be equal to transaction dot credits and the buyer ID.</p><p>
 So we know who to attribute the credits to by saying transaction dot buyer ID.</p><p>
 After that object, we can define the mode of the payment, which will be payment.</p><p>
 And very important, we need to define the success underscore URL, which will be a dynamic template string of process dot env dot next underscore public.</p><p>
 underscore server underscore URL forward slash profile.</p><p>
 So we want to go over to the profile.</p><p>
 If we're successful and on cancel URL, we simply want to go back to home, meaning forward slash like this.</p><p>
 And after all of that is done, we simply want to redirect over to a session dot URL like this.</p><p>
 And of course, this redirect we have to import at the top from next navigation.</p><p>
 So we can say import redirect from next navigation.</p><p>
 There we go.</p><p>
 That is good.</p><p>
 And this is the function that will process our payment.</p><p>
 And on top of that, we also want to create a new transaction in our database.</p><p>
 So to do that, we can say export a sync function create transaction, which will get in the transaction as a param.</p><p>
 And it's a type create transaction, perams.</p><p>
 This one will be more similar to our typical server actions, where we can say try and catch in the catch.</p><p>
 We call the handle error like this and provided the error.</p><p>
 And then in the try, we await connect to database like this.</p><p>
 Then we want to create a new transaction with a buyer ID.</p><p>
 That's going to look something like this.</p><p>
 Const new transaction is equal to a wait transaction dot create to which we pass the entire transaction.</p><p>
 We can spread it out.</p><p>
 And we also pass in the buyer as the transaction dot buyer ID.</p><p>
 And of course, this transaction has to be imported from transaction models.</p><p>
 Finally, we want to update the buyer's credits by saying await update credits coming from user actions to which we pass the transaction that buyer ID and the number of credits we want to add.</p><p>
 Finally, at the end, we want to return Jason that parse and Jason that stringify this new transaction.</p><p>
 There we go.</p><p>
 These are our two server actions.</p><p>
 Want to process the checkout and the payment and want to actually create a transaction record in our database and to attribute the credits to the user.</p><p>
 So now that we have these, we are ready to set up our stripe webhook.</p><p>
 Remember our clerks webhooks where they listen for specific events and then they trigger some actions.</p><p>
 Similarly, we also want to trigger a webhook from stripe once the payment is done so that we know to call this create transaction and then attribute the credits to the user.</p><p>
 So going back to the dashboard, we can go to webhooks and create a new endpoint.</p><p>
 We want to set the endpoint URL similar to what we have done in clerk.</p><p>
 So if we go back to clerk and to webhooks, we want to take this exact same URL, paste it here, but instead of saying clerk at the end, we'll say stripe because this will be a completely different webhook.</p><p>
 We want to subscribe to one event right here.</p><p>
 Similar to what we have done on clerk, this will be a checkout.</p><p>
session.</p><p>
compleated.</p><p>
 So we know when a checkout session has been completed, so we can do something.</p><p>
 So let's click add events and let's click add endpoint.</p><p>
 There we go.</p><p>
 It's waiting for events.</p><p>
 And here we get a signing secret from this webhook.</p><p>
 So we can copy it.</p><p>
 We can go back to the code specifically to our dot in V's.</p><p>
 And we can create a new ENV called stripe underscore webhook underscore secret.</p><p>
 And that's equal to this WH sec.</p><p>
 And then we have a specific key.</p><p>
 Once you do that, we can create this webhook file by going over to app API, webhooks, and then we want to create a new folder called stripe within webhooks.</p><p>
 And create a new route dot TS within it.</p><p>
 I'll provide you this simple webhook in the readme so you can simply copy it.</p><p>
 And the reason why I did that is because I want to explain the most important concepts that we can see here.</p><p>
 So once you have it, let's make sure that we fix the imports by once again automatically importing this create transaction.</p><p>
 I think it will fix it then.</p><p>
 There we go.</p><p>
 And let me explain what's happening here.</p><p>
 So we have a post request on our application.</p><p>
 And we're allowing this endpoint to be pinged by a webhook.</p><p>
 Once it's pinged, we try to access the body of the request.</p><p>
 We process the signatures so we know that they are legit.</p><p>
 Then we specify the event and be constructed by using the stripe dot webhooks dot construct event.</p><p>
 Then we figure out the event type.</p><p>
 And if the event type is the only one we signed up for, which in this case will be check out that session dot completed.</p><p>
 We destructure the ID, the amount total and the metadata from that transaction, form it in a way that suits our create transaction function that we have created not that long ago.</p><p>
 And then pass this newly crafted transaction object with the plan credits by our ID and all of that to create a new transaction in our database.</p><p>
 And then we simply return it.</p><p>
 Now to test all of this out, we of course have to have the page from which we can purchase the credits.</p><p>
 So let's go to app root credits and then page that the sx.</p><p>
 Since this is just the UI, you can find it in the readme down below and you can paste it right away.</p><p>
 We have a couple of things we have to fix.</p><p>
 The header is just a default import component.</p><p>
 And of course with this file, we also need a shared checkout component.</p><p>
 So let's navigate over to components, shared and create a new checkout dot tsex.</p><p>
 And then get access to it from the readme and paste it right here.</p><p>
 You'll notice that it is about a 10 line long form with a simple button that says buy credit.</p><p>
 What matters most is what is happening on the on checkout function.</p><p>
 Well, here we get the transaction information and call the checkout credits with the transaction.</p><p>
 And do remember what the checkout credits is a function we have created before.</p><p>
 This one right here where we actually trigger the stripe checkout process.</p><p>
 And if you look above, you'll see that we have a user effect to check where we have to redirect to.</p><p>
 And that is it.</p><p>
 With that in mind, we can properly import this shared checkout component so that it doesn't complain.</p><p>
 That is right here.</p><p>
 There we go.</p><p>
 And we called it a subtitle with a lowercase t.</p><p>
 So now we have a complete credits page.</p><p>
 So what do you say that we go ahead and check it out going back to our app and going to buy credits.</p><p>
 We now have this wonderful looking by credits page, which is quite simple.</p><p>
 It's just one card that we replicate three times.</p><p>
 And you can see that as well.</p><p>
 We are reusing the header.</p><p>
 We're mapping over the plans.</p><p>
 And for each one, we're simply showing an li that has an image and a label.</p><p>
 That is it.</p><p>
 Now what happens if I try to click by credit, we get an invalid URL undefined profile.</p><p>
 Your Rels must begin with htdb or htdps.</p><p>
 And the reason this is happening is if you go to our transaction, I believe that's a transaction action in checkout credits, you notice that we are using the next public server URL to navigate over somewhere.</p><p>
 So we have to add this to our ENVs as well.</p><p>
 So if we navigate over to our ENVs, we can call this something like other.</p><p>
 And then we can say next public server URL.</p><p>
 In this case, it will be equal to local host 3000.</p><p>
 Because for now, we're testing this out locally.</p><p>
 So if we try it one more time and click by credit, you can see that now we are redirected to a beautiful typical Stripe checkout.</p><p>
 You can easily process the payment by simply saying 424242 until the end, and then also type a random numbers for cvv, add a card holder, and click pay.</p><p>
 If you do this, it will process, there we go.</p><p>
 And once it does, you'll be redirected to the profile page, completing the cycle.</p><p>
 But of course, on the profile page, we cannot yet see how many credits we have.</p><p>
 So back in our code, we can go over to our app, root, profile, and then go to the page.</p><p>
 And I'll provide you with this quick profile page UI and UX.</p><p>
 You have gone this far into the video, so I doubt you'd learn anything from just writing a couple of lines of JSX.</p><p>
 So you can copy and paste it here from the readme.</p><p>
 Once you have it, we can fix the header if it's not already fixed.</p><p>
 And we need to implement one more server action within libImageActions, called getUserImages, which will allow you to extract all of the images that a specific user has created.</p><p>
 In this case, we have getAllImages that you can see here, and this one will be incredibly similar if not even easier.</p><p>
 So you can also find it in the readme down below, copy it, and paste it here.</p><p>
 The way it works is we try to connect to the database, we figure out how many pages we want to skip based off the pagination, then we only find the images by a specific author, we get all the images, return them, and that is it, your typical server action using the next JSBest Practices.</p><p>
 Now, back in here, we can use it to extract all of the user images, and would you look at this, we are reusing the header, and this component couldn't be any simpler.</p><p>
 It is just a section that shows how much credits we have available by showing the credit balance, shows how many images we have manipulated so far, and then we re-render the same collection of images which we have created before.</p><p>
 This is the same component we show on the homepage that lists out all of the other user images, but this time we provide the images only created by that user.</p><p>
 So now if we go back, you can see that we have two credits available, and we have done three image manipulations, and you can also visit all of these three posts and perform credit operations on them.</p><p>
 Now there's one gotcha here, and that is that you can see that we tried to purchase many credits, and it didn't really work.</p><p>
 That's because we haven't yet published our webhook.</p><p>
 So if we go back to Stripe, you'll see that two events have been triggered, but they both failed, because our URL of forward slash webhook, forward slash Stripe, isn't yet deployed.</p><p>
 It's not online.</p><p>
 We haven't pushed it to Versel.</p><p>
 So to finalize this and to truly make the purchasing work, as with Clark, we have to publish this endpoint to the internet so that webhooks can call it.</p><p>
 So with that in mind, and checking out our application and seeing that it is fully done, we are ready to do a final deployment of our application.</p><p>
 And then we can go ahead and test out its live version on the internet.</p><p>
 The only thing we have to do now is just go to the source control right here.</p><p>
 We can use this GitHub Copilot trick that will simply generate a commit message, say commit and sync changes.</p><p>
 Or you can just push it manually by stopping the terminal from running and then running something like gitad.</p><p>
gidcommit-m, commit message, and then git push.</p><p>
 With that in mind, if we go back to Versel and back to our project, we can see that is now building.</p><p>
 But if we're being really careful, you'll know that this is not enough.</p><p>
 If we try it out after it builds, even though it will most likely be successful and ready, it is missing something that will make it fully work.</p><p>
 And that is, it's missing the newest environment variables that we have added.</p><p>
 Every time you add new ones locally, you also have to add them right here.</p><p>
 So what we can do is go back to our ENVs right here.</p><p>
 And before I go ahead and copy all of these and add them to our final deployed project, I want to make sure to duplicate this line and update it for the deployed version.</p><p>
 Here, you want to put the URL of your new deployed application, the same one that you had on their web hooks.</p><p>
 In my case, it is hdps, call and forward slash, forward slash, imagine if I knew thatversel.</p><p>
app and you can remove this ending part right here.</p><p>
 Now I can do command A and then command C to copy all of it.</p><p>
 I can go back here and then just paste them within this field.</p><p>
 It will automatically add all the new ones.</p><p>
 But we can see it's saying that this key might expose sensitive information, share it publicly.</p><p>
 Okay, that's good.</p><p>
 Same thing for these right here.</p><p>
 So,versel is just making sure that we know what we're doing.</p><p>
 And let's click save.</p><p>
 Added environment variable successfully.</p><p>
 But it looks like it cannot re-add the ones we already have, which is okay.</p><p>
 So, let's just ensure that the ones we need indeed got added like the webhooksecret.</p><p>
 Let's try to find it.</p><p>
 Yeah, webhooksecret is here.</p><p>
 But this one was belonging to clerk, I think.</p><p>
 But there we go.</p><p>
 These ones were added just now, which means that everything is great.</p><p>
 So now we can go back to deployments.</p><p>
 And we can just re-deploy it one more time for get measure.</p><p>
 We can give it a minute until it renders.</p><p>
 And I'll be right back.</p><p>
 There we go.</p><p>
 Our app is live.</p><p>
 Let's go back to our project.</p><p>
 And click visit.</p><p>
 Hey, you'll notice that nothing changed.</p><p>
 Besides that now at the top, it doesn't say local host.</p><p>
 Because we are looking at the deployed, imagine if I, this time in a live deployed environment.</p><p>
 We can perform all kinds of different actions, search for the images we have edited so far, like flowers.</p><p>
 So, let's go ahead and test out some of the features for one last time.</p><p>
 I'm going to go with generative fill.</p><p>
 And it looks like we get an error.</p><p>
 This time it is a server side error.</p><p>
 But we are now on the deployed side.</p><p>
 So we cannot simply go through a terminal and check it out.</p><p>
 So I'm very, very happy that I've got an chance to see this error watch you to teach you how to debug server errors once you're in the deployed side by going to server logs.</p><p>
 To go to server logs, you can visit your project and then click logs right here.</p><p>
 You'll see a lot of different logs.</p><p>
 And at the bottom, there's going to be a button to load new ones.</p><p>
 Now, in this case, we're getting an error saying user not found.</p><p>
 Another user not found.</p><p>
 And then if you scroll a bit down, you'll also see an error from clerk saying that we cannot access the forward slash because there's no sign than user.</p><p>
 To fix this, we can go back to our code specifically to the middleware.</p><p>
ts file where we define our public routes.</p><p>
 And in this case, I want to make two more routes public.</p><p>
 First one is maybe the most important one, which is just the forward slash.</p><p>
 So our homepage will always be public.</p><p>
 But if we go to some different pages, then they will be private because we have to have an account to be able to create transformations.</p><p>
 But in case you want to just see the transformations, other people have made maybe to peak your interest so we can urge you to buy some credits.</p><p>
 Then that's going to be publicly available.</p><p>
 And then similar to our API webhooks clerk, we also want to add the API webhooks stripe right here so that we can freely ping it and so that our database gets updated.</p><p>
 Once you make those changes, make sure to commit and push them one more time and sync the changes.</p><p>
 Now, let's wait until our deployment is out.</p><p>
 So we can test all of the functionalities of our application and hopefully see them fully working.</p><p>
 Now, if we go back before you go ahead and try to test out some of these functionalities, just to be 100% sure we're logged in, I'm going to go back to clerk and sign out.</p><p>
 Because now we're on the final version and we want to try to truly log in through the deployed version as well.</p><p>
 So let's log in.</p><p>
 Clerk works as well.</p><p>
 You can see that we are on this new domain now.</p><p>
 So you can sign in or sign up.</p><p>
 And once you're in, head over to imagery store or whatever else you want to test out.</p><p>
 And now it works.</p><p>
 Now, I want to test out one more thing before we test out the transformations, which we have done a couple of times so far.</p><p>
 And that is check out how many credits I have and try to see what happens if we don't have enough credits to purchase new transformations.</p><p>
 To test that out, we can be a bit sneaky and go to our MongoDB Atlas collections, go to users, and then I will remove all of the credits right here.</p><p>
 Of course, I could add them as well, but in this case, I will just reset myself to zero.</p><p>
 Now, if I go back and reload, we can see that all of this is working real time.</p><p>
 It's pulling it from the live cloud database.</p><p>
 And now, if I go to, let's say, Geno to fill, we'll get this nice looking model that says insufficient credits looks like you run out of these free credits.</p><p>
 No worries.</p><p>
 You can grab more.</p><p>
 So I'm going to click yes, proceed.</p><p>
 And we are redirected to buy credits page.</p><p>
 Now, you can notice that here we're offering crazy amounts of credits, like you can even get free consumables like for 20, but immediately with 40 boxes, 120 and for 200 boxes, 2000.</p><p>
 Of course, since you will use this knowledge right here by building this video to build your own sass, you can figure out your own pricing model and then price it accordingly.</p><p>
 Of course, you want to go into cloud and re figure out how much they charge and then still make it profitable for yourself by charging for credits a bit more.</p><p>
 That's basically the only thing that this guy is doing levels IO on Twitter.</p><p>
 He's creating a sass after a sass after a sass after a sass and all of them are performing well.</p><p>
 One of the better ones he created lately is photoai.</p><p>
com.</p><p>
 Basically, it allows you to generate AI portraits.</p><p>
 Similar to what we are doing, right? You can also expand cloud in a re's AI transformation tools to build a customized and feature specific sass like he has built right here.</p><p>
 And then you'll be able to charge even more for these credits.</p><p>
 With that said, let's go ahead and test out the functionality.</p><p>
 Let's try to get 120 by clicking buy credit.</p><p>
 Since we updated our reinvies, now after we complete the transaction, it knows where it needs to redirect.</p><p>
 So, enter your information, use 4242 for the demo Stripe card and click pay.</p><p>
 It will process your payment immediately triggered the web hook, which will then call our API.</p><p>
 Our API will create a transaction database and grant you the credits.</p><p>
 That's how it works.</p><p>
 And you can see we are immediately redirected back to our profile with 120 credits available.</p><p>
 With that in mind, you can now choose to do whatever you want.</p><p>
 I'm going to go to generative fill and I will try to select a new image from unsplash.</p><p>
 But of course, you can also upload your images.</p><p>
 And let's try to go with some animals.</p><p>
 I'm going to go with this horse right here as it is a very nice horizontal image.</p><p>
 And I want to make it vertical.</p><p>
 So, I'm going to upload it.</p><p>
 Go to phone portrait.</p><p>
 If we want to use it for social media, call it a horsey.</p><p>
 And I'm going to click apply transformation.</p><p>
 We immediately get this nice looking loader.</p><p>
 And now that we see all of this in action, it truly feels like a complete SaaS application that you can just optimize and sell credits on.</p><p>
 It is that good.</p><p>
 Look at this.</p><p>
 It even added the legs and it added a fence right here, which is quite interesting.</p><p>
 That is cool.</p><p>
 And it also expanded the sky.</p><p>
 So let's go ahead and save this image.</p><p>
 Immediately, we are redirected back to that image details where we can update or delete it.</p><p>
 And it also immediately got added on our homepage as well as on our profile.</p><p>
 You can also see that credits got deducted and image manipulations done have increased.</p><p>
 So with that in mind, my friends, you have completed.</p><p>
 Imagine if I or however you decided to call it an AI image transformation tool.</p><p>
 Of course, without even mentioning it, all of this is made possible by cloud memory AI that allowed us to upload images, optimize them and also do all of these transformation on these images, which essentially made our application possible.</p><p>
 And we used clerk to optimize the user management with that in mind.</p><p>
 If you came to the end of this video, you truly are the perfect fit for what we offer on JS mastery that pro in this course, you explored only a bit of what next JS has to offer.</p><p>
 But in the course we dive deeper, I teach you how all of these biggest companies truly use next years behind the scenes and not just use it like good old react.</p><p>
 Rather, I teach you how to think in next JS and resolve all of these biggest and most common next years issues and bring your performance from this to something like this.</p><p>
 It all happens through deep code dives and truly understanding how things work with this detailed and animated graphics.</p><p>
 You built and deploy an app that is multiple times more complex than what you've done right now.</p><p>
 And we also included active lessons where you not only have to follow the videos, rather you are given a task with examples, thought process, resources and even hints.</p><p>
 So you need to follow along to be able to learn how next year's truly works.</p><p>
 And all of that is done in the process of building the amazing dev overflow, which is a complete modern stack or flow application.</p><p>
 So if this sounds like something you'd be interested in, check it out.</p><p>
 And if you're already a working developer in your junior role, then I have something even better for you within our masterclass, you will not only follow the recorded courses, rather you will build apps completely independently and on your own with its sole purpose of increasing your skills and getting a higher paying job and leveling up in your career from junior to mid and even senior positions.</p><p>
 So if what I've just said sounds interesting, definitely check out JS mastery that pro with that in mind, huge thanks to you for coming to the end of this video building it out.</p><p>
 You're truly amazing for staying all the way until the end.</p><p>
 Not many people do, but you did.</p><p>
 So great work and I'll see you in the next one.</p><p>
 Have a wonderful day.</p>


</div>
</body>
</html>
